zoukankan      html  css  js  c++  java
  • 点分治

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

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

    先看下题吧:

    一、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

    题解到时候放上。

  • 相关阅读:
    Docker的使用
    Django常见问题
    Linux系统使用
    Nginx
    Redis
    MySQL基础、主从复制、优化
    Python常见的问题
    Python基础知识
    Vue的使用
    python监控tomcat日记文件
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10389510.html
Copyright © 2011-2022 走看看