点分治

点分治是一个很有意思的东西。

一般可以用来静态地处理树上路径问题。

先看下题吧:

一、LuoguP3806模板

给定一棵有n个点的树

询问树上距离为k的点对是否存在。

怎么考虑这个问题呢?

我们不妨先任选一个点为根RT。

那么对于树上路径分成两种:跨过根RT的路径,在RT一颗子树内的路径(不经过RT)。

运用分治的思想考虑下第二种路径:

我们可以处理RT的子树,那么总有一个点为根时,它会成为第一种路径。

那么对于一个根,只处理处跨过它的路径,递归处理子树,

我们发现,这可以不重不漏的包含所有情况,且是具有相同子结构的。

然后以谁为根能够最优呢???

其实我们发现,这个递归的次数是与树的深度有关的。

所以当我们以这颗树的重心为根就能够保证只递归logN次。。。

接下来的问题就是,怎样求路径,且判断路径长度是否为k。

假设当前递归到了RT为根的子树。

有两种方法可以求这条路径:

①dfs一遍求出所有的点到根的距离,然后sort一遍,

两个指针从两边往中间扫,那么就可以开心的找出所有的等于k的路径O(∩_∩)O~~啦。。。

开心。。。

啦吗??

其实我们也很容易注意到:在一棵子树内为k的路径也会被统计进来。。

所以我们需要把它给判掉,很简单的。

 

②我们发现路径长度并不大,所以考虑开一个桶。

我们依次遍历每棵子树,遍历过程中计算答案,

即和之前已经遍历过了的子树计算贡献。

然后遍历完后把它的路径加入桶就好了。

这样既不重不漏,又不用担心来自一颗子树的问题,舒服。

 

所以,我们点分治模板就搞完了。。。。。。

 

 1 #include<bits/stdc++.h>
 2 #define RG register
 3 #define IL inline
 4 #define DB double 
 5 #define LL long long
 6 using namespace std;
 7 
 8 IL int gi() {
 9     RG int x=0,w=0; char ch=0;
10     while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
11     while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
12     return w?-x:x;
13 }
14 
15 const int N=1e4+10;
16 const int MAX=1e7+10;
17 
18 int jud[MAX],qur[N],ans[N];
19 int num,a[N],b[N],dis[N];
20 int n,m,RT,now,tot,head[N],vis[N],siz[N];
21 
22 IL bool cmp(int x,int y) {return dis[a[x]]<dis[a[y]];}
23 
24 struct Edge{int next,to,ver;}e[N<<1];
25 IL void make(int a,int b,int c) {
26     e[++tot]=(Edge){head[a],b,c},head[a]=tot;
27     e[++tot]=(Edge){head[b],a,c},head[b]=tot;    
28 }
29 
30 void Get_size(int x,int fx) {
31     RG int i,y;
32     for (i=head[x],siz[x]=1;i;i=e[i].next) {
33         if ((y=e[i].to)==fx) continue;
34         Get_size(y,x),siz[x]+=siz[y];
35     }
36 }
37 
38 void Get_root(int x,int fx,int S) {
39     RG int i,y,Mpr=S-siz[x];    
40     for (i=head[x];i;i=e[i].next) {
41         if ((y=e[i].to)==fx||vis[y]) continue;
42         Get_root(y,x,S),Mpr=max(Mpr,siz[y]);
43     }
44     if (Mpr<now) now=Mpr,RT=x;
45 }
46 
47 void dfs(int x,int fx) {
48     RG int i,y;
49     a[x]=++num,b[num]=x;
50     for (i=1;i<=m;++i)
51         if (qur[i]>=dis[x]) ans[i]|=jud[qur[i]-dis[x]];
52     for (i=head[x];i;i=e[i].next) {
53         if ((y=e[i].to)==fx||vis[y]) continue;
54         dis[y]=dis[x]+e[i].ver,dfs(y,x);        
55     }
56 }
57 
58 void solve(int x) {
59     RG int i,j,y;
60     Get_size(x,0),now=n,Get_root(x,0,siz[x]);
61     dis[RT]=0,jud[0]=1;
62     for (i=head[RT],vis[RT]=1;i;i=e[i].next) {
63         if (vis[y=e[i].to]) continue;
64         dis[y]=dis[RT]+e[i].ver,dfs(y,RT);
65         for (j=a[y];j<=num;++j) jud[dis[b[j]]]=1;
66     }
67        for (;num;--num) jud[dis[b[num]]]=0;
68     for (i=head[RT];i;i=e[i].next) 
69         if (!vis[y=e[i].to]) solve(y);
70 }
71 
72 int main ()
73 {
74     RG int i,x,y,z;
75     n=gi(),m=gi();
76     for (i=1;i<n;++i)
77         x=gi(),y=gi(),z=gi(),make(x,y,z);
78     for (i=1;i<=m;++i) qur[i]=gi();
79     solve(1);
80     for (i=1;i<=m;++i) puts(ans[i]?"AYE":"NAY");
81     return 0;
82 }
83 // size 必须每次重新求 因为每个点的子树有可能改变了
84 // 这样才能保证每次选的都是重心 否则很容易被链给卡掉
BY BHLLX

 

洛谷上这个题的数据是真的水,我叫了两遍错的都A了。

一定要仔细写好了,略略略别学了一个假的点分治~~~~(>_<)~~~~。

 

放几道入门题吧(作为初学者的我还没很多好题。。。):

一、Race

二、Distance in Tree

三、Tree

题解到时候放上。

 

posted @ 2019-02-16 22:12  薄荷凉了夏  阅读(162)  评论(0编辑  收藏  举报