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