树的分治之点分治

点分治我感觉是图论树部分比较考验脑力的一种题目了

POJ1741

题意:给一棵边带权树,问两点之间的距离小于等于K的点对有多少个

满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中

对于根节点进行一次dfs,求出deep,并将其从小到大排序

void getdeep(int x,int fa)
{
    dep[++dep[0]]=d[x];
    for(int tmp=g[x];tmp;tmp=e[tmp].next)
    {
        if(e[tmp].t==fa||vis[e[tmp].t]) continue;
        d[e[tmp].t]=d[x]+e[tmp].v;
        getdeep(e[tmp].t,x);
    }
}

然后看一下calculate

int cal(int x,int tmp)
{
    d[x]=tmp;dep[0]=0;
    getdeep(x,0);
    sort(dep+1,dep+dep[0]+1);
    int t=0,l,r;
    for(l=1,r=dep[0];l<r;)
    {
        if(dep[l]+dep[r]<=k) {t+=r-l;l++;}
        else r--;
    }
    return t;
} 

如果我们已经知道了此时所有点到根的距离a[i]

a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出

然后根据分治的思想,分别对所有的儿子求一遍即可

void work(int x)  //点分治 
{
    ans+=cal(x,0);
    vis[x]=1;
    for(int tmp=g[x];tmp;tmp=e[tmp].next)
    {
        if(vis[e[tmp].t]) continue;
        ans-=cal(e[tmp].t,e[tmp].v);
        sum=ch[e[tmp].t];
        root=0;
        getroot(e[tmp].t,root);
        work(root);
    }
}

但是这会出现重复的——当前情况下两个点位于一颗子树中,那么应该将其减掉

显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算

在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心

这样,所有的子树规模就会变的很小了。时间复杂度O(Nlog^2N)

void getroot(int x,int fa)
{
    ch[x]=1;f[x]=0;
    for(int tmp=g[x];tmp;tmp=e[tmp].next)
    {
        if(e[tmp].t==fa||vis[e[tmp].t]) continue;
        getroot(e[tmp].t,x);
        ch[x]+=ch[e[tmp].t];
        f[x]=max(f[x],ch[e[tmp].t]);
    }
    f[x]=max(f[x],sum-ch[x]);
    if(f[x]<f[root]) root=x;
}

有了板子不能救命,还要大量刷大量刷才可以,点分治确实是一个要命的地方,上次的省赛的一道题就栽了

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 using namespace std;
  5 const int maxn=10005;
  6 const int maxm=20005;
  7 const int INF=0x7fffffff;
  8 int n,k,ans;
  9 int cnt,g[maxn];
 10 struct Edge{int t,next,v;}e[maxm];
 11 
 12 int root,sum;
 13 int ch[maxn],f[maxn],dep[maxn],d[maxn];
 14 bool vis[maxn];
 15 
 16 void addedge(int u,int v,int w)
 17 {
 18     cnt++;
 19     e[cnt].t=v;e[cnt].v=w;
 20     e[cnt].next=g[u];g[u]=cnt;
 21 }
 22 int read()
 23 {
 24     int x=0,f=1;char ch=getchar();
 25     while(ch<'0'||ch>'9') {if(ch=='0')f=-1;ch=getchar();}
 26     while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
 27     return x*f;
 28 }
 29 void getroot(int x,int fa)
 30 {
 31     ch[x]=1;f[x]=0;
 32     for(int tmp=g[x];tmp;tmp=e[tmp].next)
 33     {
 34         if(e[tmp].t==fa||vis[e[tmp].t]) continue;
 35         getroot(e[tmp].t,x);
 36         ch[x]+=ch[e[tmp].t];
 37         f[x]=max(f[x],ch[e[tmp].t]);
 38     }
 39     f[x]=max(f[x],sum-ch[x]);
 40     if(f[x]<f[root]) root=x;
 41 }
 42 void getdeep(int x,int fa)
 43 {
 44     dep[++dep[0]]=d[x];
 45     for(int tmp=g[x];tmp;tmp=e[tmp].next)
 46     {
 47         if(e[tmp].t==fa||vis[e[tmp].t]) continue;
 48         d[e[tmp].t]=d[x]+e[tmp].v;
 49         getdeep(e[tmp].t,x);
 50     }
 51 }
 52 int cal(int x,int tmp)
 53 {
 54     d[x]=tmp;dep[0]=0;
 55     getdeep(x,0);
 56     sort(dep+1,dep+dep[0]+1);
 57     int t=0,l,r;
 58     for(l=1,r=dep[0];l<r;)
 59     {
 60         if(dep[l]+dep[r]<=k) {t+=r-l;l++;}
 61         else r--;
 62     }
 63     return t;
 64 } 
 65 void work(int x)  //点分治 
 66 {
 67     ans+=cal(x,0);
 68     vis[x]=1;
 69     for(int tmp=g[x];tmp;tmp=e[tmp].next)
 70     {
 71         if(vis[e[tmp].t]) continue;
 72         ans-=cal(e[tmp].t,e[tmp].v);
 73         sum=ch[e[tmp].t];
 74         root=0;
 75         getroot(e[tmp].t,root);
 76         work(root);
 77     }
 78 }
 79 void init()
 80 {
 81     ans=root=cnt=0;
 82     memset(vis,0,sizeof(vis));
 83     memset(g,0,sizeof(g));
 84 }
 85 int main()
 86 {
 87     int x,y,z;
 88     while(1)
 89     {
 90         init();
 91         n=read();k=read();
 92         if(n==0) break;
 93         for(int i=1;i<n;i++)
 94         {
 95             x=read();y=read();z=read();
 96             addedge(x,y,z);
 97             addedge(y,x,z);
 98         }
 99         sum=n;
100         f[0]=INF;
101         getroot(1,0);
102         work(root);
103         printf("%d\n",ans);
104     }
105 
106     return 0;
107 }

猜你喜欢

转载自www.cnblogs.com/aininot260/p/9461358.html