題目描述
給定一棵有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 的點對。這道題我們用分治的方法。那我們假設要解決乙個樹,那我們先選重心作為根節點 為了減少高度節省時間 然後就分成兩種討論,一種是路徑都在同乙個子樹中,那這個我們就可以把問題轉換為解決這個子樹。另一種就是在兩個不同的子樹中 ...