点分治(树分治)
文章目錄
- 介紹:
- 題目:
- 做法:
- 模板題 [P3806 【模板】點分治1](https://www.luogu.com.cn/problem/P3806)
- 代碼:
介紹:
將原問題分解成若干相同形式,相互獨立的子問題,各個擊破
一般用來解決有關樹上路徑的統計和詢問
題目:
P4178 Tree
給定一棵 n 個節點的樹,每條邊有邊權,求出樹上兩點距離小于等于 k 的點對數量。
做法:
暴力做法;(O(n2))
點分治做法:
選擇一個點作為分治中心,令其為rt做dfs。對于一條路徑path(u,v),其要么經過rt(即lca(u,v) = = rt),要么在某個子樹sub(son[rt])中
把問題形式化為:
對T數進行分治work(T)的步驟:
1.找到一個分治中心rt
2.ans+=solve(T,rt)//統計答案(統計所有穿過化的路徑)
3.對所有rt的子節點v,遞歸調用work(v)
所有合法路徑在上述分治過程中被不重不漏地統計到
詳細過程:
假設高度一共有h層,經過h層遞歸后到達邊界,每一層子問題互不重疊,
每一層都是O(N)
總復雜度:O(H*N)
我們控制H的大小
(H = 遞歸的層數)
點分治的復雜度被以下兩個條件保證:
1.h=O(log n),每次選T的重心作為rt(重心滿足刪除后形成的子樹大小為之前一半)
2.找重心以及統計答案solve(T,rt)的復雜度=O(size(T)),或者帶log,不與n相關
條件1保證每遞歸一層size(T)減半,log層到達邊界
條件2保證每層復雜度為O(n)或者O(nlog n)
點分治總復雜度 O(log n )或O(nlog2n),取決于solve是否帶log。
模板題 P3806 【模板】點分治1
題目描述
給定一棵有 n 個點的樹,詢問樹上距離為 k 的點對是否存在。
代碼:
//niiick #include<iostream> #include<vector> #include<algorithm> #include<queue> #include<cstring> #include<cstdio> using namespace std;int read() {int f=1,x=0;char ss=getchar();while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}return f*x; }const int inf=10000000; const int maxn=100010; int n,m; struct node{int v,dis,nxt;}E[maxn<<1]; int tot,head[maxn]; int maxp[maxn],size[maxn],dis[maxn],rem[maxn]; int vis[maxn],test[inf],judge[inf],q[maxn]; int query[1010]; int sum,rt; int ans;void add(int u,int v,int dis) {E[++tot].nxt=head[u];E[tot].v=v;E[tot].dis=dis;head[u]=tot; }void getrt(int u,int pa)//求重心 {size[u]=1; maxp[u]=0;for(int i=head[u];i;i=E[i].nxt) {int v=E[i].v;if(v==pa||vis[v]) continue;getrt(v,u);size[u]+=size[v];maxp[u]=max(maxp[u],size[v]);}maxp[u]=max(maxp[u],sum-size[u]);if(maxp[u]<maxp[rt]) rt=u; }void getdis(int u,int fa)//每一個子節點到根的距離 {rem[++rem[0]]=dis[u];for(int i=head[u];i;i=E[i].nxt){int v=E[i].v;if(v==fa||vis[v])continue;dis[v]=dis[u]+E[i].dis;getdis(v,u);} }void calc(int u) {int p=0;for(int i=head[u];i;i=E[i].nxt){int v=E[i].v;if(vis[v])continue;rem[0]=0; dis[v]=E[i].dis;getdis(v,u);//處理u的每個子樹的disfor(int j=rem[0];j;--j)//遍歷當前子樹的disfor(int k=1;k<=m;++k)//遍歷每個詢問{if(query[k]>=rem[j])test[k]|=judge[query[k]-rem[j]];//如果query[k]-rem[j]的路徑存在就標記第k個詢問}for(int j=rem[0];j;--j)//保存出現過的dis于judge{q[++p]=rem[j];judge[rem[j]]=1;}}for(int i=1;i<=p;++i)//處理完這個子樹就清空judgejudge[q[i]]=0;//特別注意一定不要用memeset,會T}void solve(int u) { //judge[i]表示到根距離為i的路徑是否存在vis[u]=judge[0]=1; calc(u);//處理以u為根的子樹for(int i=head[u];i;i=E[i].nxt)//對每個子樹進行分治{int v=E[i].v;if(vis[v])continue;sum=size[v]; maxp[rt=0]=inf;//注意sum是以v為根的子樹大小getrt(v,0); solve(rt);//在子樹中找重心并遞歸處理} }int main() {n=read();m=read();for(int i=1;i<n;++i){int u=read(),v=read(),dis=read();add(u,v,dis);add(v,u,dis);}for(int i=1;i<=m;++i)query[i]=read();//先記錄每個詢問以離線處理maxp[rt]=sum=n;//第一次先找整棵樹的重心getrt(1,0); solve(rt);//對樹進行點分治for(int i=1;i<=m;++i){if(test[i]) printf("AYE\n");else printf("NAY\n");}return 0; }總結
- 上一篇: 局域网如何共享文件 局域网文件共享的图文
- 下一篇: POJ2155 - Matrix(二维树