[模板]点分治

Posted ressed

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[模板]点分治相关的知识,希望对你有一定的参考价值。

用途

大规模地处理树上路径

做法

先考虑对x为根的子树做dfs来处理x子树到x的路径,然后统计答案,然后再递归地做x的儿子...

然而当树退化成链时,最差复杂度是$O(n^2)$的

类比一维中二分的做法,其实是使左右区间尽量平均,那我们也让我们要处理的点的子树大小尽量平均

具体来说,我们每次想要做x这个子树的时候,会想先在这个子树中找到一个点root,使得它的每个子树大小的最大值最小,然后将root作为这棵子树的新根,再继续处理路径

我们通过dfs来统计子树的大小以选定root,需要注意的是,由于某个点的父亲将来也有可能变成它的孩子(如果它被选为根的话),那就也需要算父亲作为它的孩子时的大小,其实就是S-size[x],其中S是这棵树的总大小

这样复杂度就是$O(nlogn)$的了

然而还有一个很重要的问题:我在以x为根去dfs找路径的时候,可能会找到两个端点在同一个子树的情况,这显然是不合法的。我们可以有两种方法来处理这种情况

1.(适用于大多数情况)在处理x的子树y的时候,先钦定住要走x到y这条边,也同样计算一波路径,这样算出来的就一定是刚才的不合法情况,减掉就完事了

2.(适用于可以单独地对于某条路径O(1)地统计答案)我不做x,而是对x的每个儿子y,先用子树y统计答案,再用子树y更新答案,为以后的统计铺路,这样做,我统计到的答案一定不在同一个子树里,也就不会出现上面的问题。(例:bzoj2599 Race

例题:

luogu4178 求距离不超过K的路径数

技术分享图片
 1 #include<bits/stdc++.h>
 2 #define pa pair<int,int>
 3 #define ll long long
 4 using namespace std;
 5 const int maxn=40040;
 6 
 7 inline ll rd(){
 8     ll x=0;char c=getchar();int neg=1;
 9     while(c<0||c>9){if(c==-) neg=-1;c=getchar();}
10     while(c>=0&&c<=9) x=x*10+c-0,c=getchar();
11     return x*neg;
12 }
13 
14 struct Edge{
15     int a,b,l,ne;
16 }eg[maxn*2];
17 int egh[maxn],ect;
18 int N,K;
19 int mins,smsiz,pct;
20 int siz[maxn],dis[maxn],ans,fa[maxn];
21 bool flag[maxn];
22 
23 inline void adeg(int a,int b,int l){
24     eg[++ect].a=a;eg[ect].b=b;eg[ect].l=l;
25     eg[ect].ne=egh[a];egh[a]=ect;
26 }
27 
28 void getroot(int &rt,int x){
29     int mm=0;siz[x]=1;
30     for(int i=egh[x];i!=-1;i=eg[i].ne){
31         int b=eg[i].b;if(b==fa[x]||flag[b]) continue;
32         fa[b]=x;getroot(rt,b);
33         siz[x]+=siz[b];mm=max(siz[b],mm);
34     }mm=max(smsiz-siz[x],mm);
35     if(mm<mins) mins=mm,rt=x;
36 }
37 
38 void getdis(int x,int f,int d){
39     dis[++pct]=d;
40     for(int i=egh[x];i!=-1;i=eg[i].ne){
41         int b=eg[i].b;if(b==f||flag[b]) continue;
42         getdis(b,x,eg[i].l+d);
43     }
44 }
45 
46 int calc(int x,int tt){
47     pct=0;getdis(x,0,tt);int re=0;
48     sort(dis+1,dis+pct+1);
49     int l=1,r=pct;
50     while(l<r){
51         if(dis[l]+dis[r]<=K) re+=r-l,l++;
52         else r--;
53     }return re;
54 }
55 
56 void solve(int x){
57     flag[x]=1;
58     ans+=calc(x,0);
59     for(int i=egh[x];i!=-1&&i;i=eg[i].ne){
60         int b=eg[i].b;if(flag[b]) continue;
61         ans-=calc(b,eg[i].l);
62         mins=0x3f3f3f3f,smsiz=siz[b];
63         int root=0;getroot(root,b);
64         siz[fa[root]]=smsiz-siz[root];
65         solve(root);
66     }
67 }
68 
69 int main(){
70     int i,j,k;
71     //freopen("4178.in","r",stdin);
72     N=rd();memset(egh,-1,sizeof(egh));
73     for(i=1;i<N;i++){
74         int a=rd(),b=rd(),c=rd();
75         adeg(a,b,c);adeg(b,a,c);
76     }K=rd();
77     smsiz=N;mins=0x3f3f3f3f;
78     int root=0;getroot(root,1);
79     solve(root);
80     printf("%d
",ans);
81 
82     return 0;
83 }
luogu4178

bzoj2599/luogu4149 求距离为K的路径的最小边数

技术分享图片
 1 #include<bits/stdc++.h>
 2 #define pa pair<int,int>
 3 #define ll long long
 4 using namespace std;
 5 const int maxn=200020,maxk=1000010;
 6 
 7 inline ll rd(){
 8     ll x=0;char c=getchar();int neg=1;
 9     while(c<0||c>9){if(c==-) neg=-1;c=getchar();}
10     while(c>=0&&c<=9) x=x*10+c-0,c=getchar();
11     return x*neg;
12 }
13 
14 int N,K;
15 struct Edge{
16     int b,ne,l;
17 }eg[maxn*2];
18 int egh[maxn],ect;
19 int siz[maxn],pct;
20 bool flag[maxn];
21 int root,ms,smsiz;
22 int dis[maxk],ans=0x3f3f3f3f;
23 queue<int> dq;
24 
25 
26 inline void adeg(int a,int b,int l){
27     eg[++ect].b=b;eg[ect].l=l;eg[ect].ne=egh[a];egh[a]=ect;
28 }
29 
30 void getroot(int x,int f){
31     siz[x]=1;int mm=0;
32     for(int i=egh[x];i;i=eg[i].ne){
33         int b=eg[i].b;if(flag[b]||b==f) continue;
34         getroot(b,x);
35         siz[x]+=siz[b];mm=max(siz[b],mm);
36     }mm=max(smsiz-siz[x],mm);
37     if(mm<ms) ms=mm,root=x;
38 }
39 
40 void getdis(int x,int f,int d,int dep){
41     if(d<=K){
42         if(dis[d]>2e5) dq.push(d);
43         dis[d]=min(dis[d],dep);
44     }else return;
45     for(int i=egh[x];i;i=eg[i].ne){
46         int b=eg[i].b;if(flag[b]||b==f) continue;
47         getdis(b,x,d+eg[i].l,dep+1);
48     }
49 }
50 void calans(int x,int f,int d,int dep){
51     if(d<=K){
52         if(dis[K-d]<=2e5) ans=min(ans,dep+dis[K-d]);
53     }else return;
54     for(int i=egh[x];i;i=eg[i].ne){
55         int b=eg[i].b;if(flag[b]||b==f) continue;
56         calans(b,x,d+eg[i].l,dep+1);
57     }
58 }
59 
60 inline void solve(int x){
61     flag[x]=1;
62     for(int i=egh[x];i;i=eg[i].ne){
63         int b=eg[i].b;if(flag[b]) continue;
64         calans(b,x,eg[i].l,1);
65         getdis(b,x,eg[i].l,1);
66     }while(!dq.empty()) dis[dq.front()]=0x3f3f3f3f,dq.pop();
67     int ss=smsiz;
68     for(int i=egh[x];i;i=eg[i].ne){
69         int b=eg[i].b;if(flag[b]) continue;
70         ms=0x3f3f3f3f;smsiz=siz[b]>siz[x]?ss-siz[x]:siz[b];
71         getroot(b,x);solve(root);
72     }
73 }
74 
75 int main(){
76     int i,j,k;
77     N=rd(),K=rd(); 
78     for(i=1;i<N;i++){
79         int a=rd()+1,b=rd()+1,c=rd();
80         adeg(a,b,c);adeg(b,a,c);
81     }memset(dis,127,sizeof(dis));dis[0]=0;
82     smsiz=N;ms=0x3f3f3f3f;getroot(1,0);
83     solve(root);
84     if(ans<=2e5) printf("%d
",ans);
85     else printf("-1
");
86     return 0;
87 }
bzoj2599

 

以上是关于[模板]点分治的主要内容,如果未能解决你的问题,请参考以下文章

点分治模板理解

[P3806] 模板点分治 - 点分治

LuoguP3806 模板点分治1 (点分治)

洛谷.3806.[模板]点分治1(点分治)

模板点分治

luoguP3806 模板点分治1 [点分治]