JZOJ 1166 樹中點對距離

2021-07-22 12:49:56 字數 4376 閱讀 7971

給出一棵帶邊權的樹,問有多少對點的距離<=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 就是乙個合法的點...