1468: Tree 点分治

学习了一下点分治。
与树链剖分的思想差不多,我们把分治策略应用到树上。
比如这个题,我们要求的是一些合法的路径,这些路径我们可以分为两种:过某个点和不过某个点。这样我们就可以分别来求这两种情况。
如果我们已经知道了此时所有点到根的距离d[i],d[x] + d[y] <= k的(x,y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出。然后根据分治的思想,分别对所有的儿子求一遍即可,但是这会出现重复的——当前情况下两个点位于一颗子树中,那么应该将其减掉,因为在对子树进行求解的时候,会重新计算。
至于“某个点”的选择,为了保证复杂度,我们应选择树的重心。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 1000000007
using namespace std;
int n,k,cnt,sum,ans,root;
int head[40005],deep[40005],d[40005],f[40005],size[40005];
bool vis[40005];
int next[80005],list[80005],key[80005];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void insert(int x,int y,int z)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
    key[cnt]=z;
}
void getroot(int x,int fa) //寻找树的重心
{
    size[x]=1; f[x]=0;
    for (int i=head[x];i;i=next[i])
        if (list[i]!=fa&&!vis[list[i]])
        {
            getroot(list[i],x);
            size[x]+=size[list[i]];
            f[x]=max(f[x],size[list[i]]);
        }
    f[x]=max(f[x],sum-size[x]);
    if (f[x]<f[root]) root=x;
}
void getdeep(int x,int fa)
{
    deep[++deep[0]]=d[x];
    for (int i=head[x];i;i=next[i])
        if (list[i]!=fa&&!vis[list[i]])
        {
            d[list[i]]=d[x]+key[i];
            getdeep(list[i],x);
        }
}
inline int calc(int x,int now)
{
    d[x]=now; deep[0]=0;
    getdeep(x,0);
    sort(deep+1,deep+deep[0]+1);
    int t=0,l=1,r=deep[0];
    while (l<r)
        if (deep[l]+deep[r]<=k) t+=r-l,l++; else r--;
    return t;
}
void work(int x)
{
    ans+=calc(x,0); 
    vis[x]=1;
    for (int i=head[x];i;i=next[i])
        if (!vis[list[i]])
        {
            ans-=calc(list[i],key[i]);
            sum=size[list[i]];
            root=0;
            getroot(list[i],0);
            work(root);
        }
}
int main()
{
    n=read(); 
    for (int i=1,u,v,w;i<n;i++)
        u=read(),v=read(),w=read(),insert(u,v,w),insert(v,u,w);
    k=read();
    sum=n; f[0]=inf;
    getroot(1,0);
    work(root);
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(1468: Tree 点分治)