給出一棵帶邊權的樹,問有多少對點的距離<=le
n 2 <=
n<=
10000
,len
<231
這是一道十分經典的點分治題目。
點分治,顧名思義,就是按照點來分治。
假設當前要計算某棵子樹裡面滿足題目條件的點對數,我們可以先找出這棵子樹的重心。
易知,此棵子樹的點對數=經過重心的合法點對數+不經過重心的合法點對數。
我們先求經過重心的合法點對數,計算出此子樹裡面所有的點到重心的距離,計入陣列di
s ,然後排序。計算這顆子樹的答案時,維護兩個指標
i ,j,
i 與
j的聯絡如下,滿足di
si+dis
j <=le
n 且
j 盡量大。
可以證明,
i向右移動時,
j 一定不會向右移。
我們就可以藉此求出答案,迴圈終止條件為
i>
j 。
大概**如下:
i:=0;
j:=m; // dis陣列的長度為m
while ido
begin
inc(i);
while dis[i]+dis[j]>len do dec(j);
if i>=j then
break;
ans:=ans+(j-i);
end;
但我們會發現,這樣做的話答案會wr
ong,這是因為我們可能把同一顆子樹裡的點對算重複了,
於是我們求答案時需要多維護乙個陣列把同一棵子樹的非法點對減掉,這樣就可以了。
找完經過重心的點對答案後,就剩不經過重心的合法點對數,然後我們按照重心把此棵子樹分成多棵子樹,分治下去,對分出來的子樹執行上述操作統計答案即可。
var
bz:array[0..12000] of boolean;
en,ph,size,qz:array[0..12000] of longint;
px:array[0..20000,1..2] of int64;
bj:array[0..20000,1..3] of longint;
len,n,m,j,k,l,i,o,p,cqy,xd,ans:longint;
procedure
qsortpx
(l,r:longint);
var i,j,m:longint;
begin
i:=l;
j:=r;
m:=px[(l+r) div
2,1];
repeat
while px[i,1]do inc(i);
while px[j,1]>m do dec(j);
if i<=j then
begin
px[0]:=px[i];
px[i]:=px[j];
px[j]:=px[0];
inc(i);
dec(j);
end;
until i>j;
if lthen qsortpx(l,j);
if ithen qsortpx(i,r);
end;
procedure
qsort
(l,r:longint);
var i,j,m:longint;
begin
i:=l;
j:=r;
m:=bj[(l+r) div
2,1];
repeat
while bj[i,1]do inc(i);
while bj[j,1]>m do dec(j);
if i<=j then
begin
bj[0]:=bj[i];
bj[i]:=bj[j];
bj[j]:=bj[0];
inc(i);
dec(j);
end;
until i>j;
if lthen qsort(l,j);
if ithen qsort(i,r);
end;
function
bl(o,dx:longint):longint;
vari:longint;
begin
size[o]:=1;
for i:=en[o-1]+1
to en[o] do
if bz[bj[i,2]] and (ph[bj[i,2]]<>xd) then
begin
ph[bj[i,2]]:=xd;
bl:=bl(bj[i,2],dx);
if bl>0
then
exit;
size[o]:=size[o]+size[bj[i,2]];
end;
if size[o]>=dx-size[o] then
exit(o);
exit(0);
end;
procedure
cqyilo
(o,cq,qy:longint);
var i,j,k:longint;
begin
inc(cqy);
px[cqy,1]:=cq;
px[cqy,2]:=qy;
size[o]:=1;
for i:=en[o-1]+1
to en[o] do
if bz[bj[i,2]] and (ph[bj[i,2]]<>xd) then
begin
ph[bj[i,2]]:=xd;
cqyilo(bj[i,2],cq+bj[i,3],qy);
size[o]:=size[o]+size[bj[i,2]];
end;
end;
procedure
dg(o,dd:longint);
var i,j,l,k,p:longint;
begin
inc(xd);
k:=bl(o,dd);
bz[k]:=false;
cqy:=0;
inc(xd);
for i:=en[k-1]+1
to en[k] do
if bz[bj[i,2]] then
begin
ph[bj[i,2]]:=xd;
cqyilo(bj[i,2],bj[i,3],i-en[k-1]);
qz[i-en[k-1]]:=size[bj[i,2]];
end;
qsortpx(1,cqy);
for i:=1
to cqy do
if px[i,1]<=len then inc(ans)
else
break;
i:=0;
j:=cqy;
px[0,1]:=-maxlongint;
while ido
begin
inc(i);
dec(qz[px[i,2]]);
while px[i,1]+px[j,1]>len do
begin
dec(qz[px[j,2]]);
dec(j);
end;
if i>=j then
break;
ans:=ans+(j-i-qz[px[i,2]]);
end;
for i:=en[k-1]+1
to en[k] do
if (size[bj[i,2]]>1) and bz[bj[i,2]] then
dg(bj[i,2],size[bj[i,2]]);
end;
begin
readln(n,len);
for i:=1
to n-1
dobegin
readln(bj[i*2-1,1],bj[i*2-1,2],bj[i*2-1,3]);
bj[i*2,1]:=bj[i*2-1,2];
bj[i*2,2]:=bj[i*2-1,1];
bj[i*2,3]:=bj[i*2-1,3];
inc(en[bj[i*2,1]]);
inc(en[bj[i*2,2]]);
end;
for i:=1
to n do
en[i]:=en[i-1]+en[i];
qsort(1,2*n-2);
for i:=1
to n do
bz[i]:=true;
dg(1,n);
writeln(ans);
end.
jzoj1166 樹中點對距離 點分治
求一棵樹上有多少條路徑長度 le n leq len len 首先普通點分治。掃瞄的時候將每個點儲存為乙個二元組 di s,gr a dis,gra dis,g ra 分別表示離 分治到的 根的距離,屬於根的那顆子樹。然後按照dis disdi s排序,兩個指標l,r l,rl,r用cn icn i...
樹中點對距離
在一顆n個結點的樹上,統計有多少點對最短距離 m。點對不存在順序性 n 10000 我們選取乙個點x作根,那麼任何點對都分成兩種型別 1 經過x 2 不經過x 我們對經過x的進行統計,對於不經過x的繼續在x的子樹中分治下去。這就是點分治。我們處理出每個點的深度,排序後就很容易統計經過x的個數。不過有...
樹中點對距離(點分治)
給出一棵帶邊權的樹,問有多少對點的距離 len 這是一道點分治的經典題目,可以給點分治的初學者練手。點分治,顧名思義就是把每個點分開了處理答案。假設,目前做到了以x為根的子樹。先求出子樹中每個點到根的距離di s 對於兩個點 i 和 j,如果di si d isj k 那麼 i j 就是乙個合法的點...