luogu P3806 模板 點分治1

2021-09-25 04:20:16 字數 1643 閱讀 1000

題目描述

給定一棵有n個點的樹

詢問樹上距離為k的點對是否存在。

(多次詢問&&可離線)

我們先隨意指定乙個虛擬根root,將這棵樹轉化成無根樹

樹上的路徑可以分為兩類,

1.經過根節點u的路徑

2.完全在u子樹裡(不經過u)的

對於1,用dis表示當前結點到根節點root的路徑長度, 則root的子樹中兩個節點u到v的路徑長即為dis[u]+dis[v]

對於2,u到v的路徑完全在root的某個子樹內, 那麼就找到這棵子樹的根,對它再求一次第一種路徑

就是不斷的尋找重心,把原來的樹分成很多小的樹,並對每個子樹分別求解

maxx[u]表示u的兒子的子樹中,最大的大小

則樹的重心就是maxx值最小的那個節點

1 #include2 #include3

#define maxn 100010

4using

namespace

std;

5int

n,m,text[maxn],t[maxn],root;

6int

sum,size[maxn],maxx[maxn];

7int head[maxn*3],nxt[maxn*3],to[maxn*3],val[maxn*3

],cnt;

8int vis[maxn],vis1[maxn],vis2[maxn],dis[maxn],judge[10000010];9

void add(int a,int b,int

v)10

17void getroot(int u,int fa)//

找到以u為跟的子樹的重心

1827 maxx[u]=max(maxx[u],sum-maxx[u]);//

儲存u把整棵樹分成兩部分後大的部分的大小

28if(maxx[u]//

重心幾乎把樹分成平均兩部分

29return;30

}31void getdis(int u,int

f)32

40return;41

}42void qiu(int

u)43 61

}62for(int i=1;i<=vis1[0];i++) judge[vis1[i]]=0;//

下一次不經過u及其子樹,刪去因此存在的路徑長度

63return;64

} 65

void solve(int

u) //進行點分治

6679

return;80

}81intmain()

8291

for(int i=1;i<=m;i++)

92 scanf("

%d",&text[i]); //

離線計算 ,儲存每個k

93 maxx[root]=n;

94 sum=n;

95 getroot(1,0); //

找到整顆樹的重心

96 solve(root); //

由整顆樹的重心開始點分治

97for(int i=1;i<=m;i++)

98102

return0;

103 }

Luogu P3806 模板 點分治1

給定一棵有 n nn 個點,邊權的樹,回答 m mm 個詢問,每次詢問樹上距離為 k kk 的點對是否存在。資料範圍n 1 04,m 100,邊權 10000,k 107 n leqslant 10 4,m leqslant 100,texttt leqslant 10000,k leqslant ...

luogu P3806 模板 點分治1

給你一棵樹,路徑有長度,多次詢問,每次給出 k,問你是否存在路徑長度為 k 的點對。這道題我們用分治的方法。那我們假設要解決乙個樹,那我們先選重心作為根節點 為了減少高度節省時間 然後就分成兩種討論,一種是路徑都在同乙個子樹中,那這個我們就可以把問題轉換為解決這個子樹。另一種就是在兩個不同的子樹中 ...

luogu P3806 模板 點分治1

給你一棵樹,路徑有長度,多次詢問,每次給出 k,問你是否存在路徑長度為 k 的點對。這道題我們用分治的方法。那我們假設要解決乙個樹,那我們先選重心作為根節點 為了減少高度節省時間 然後就分成兩種討論,一種是路徑都在同乙個子樹中,那這個我們就可以把問題轉換為解決這個子樹。另一種就是在兩個不同的子樹中 ...