洛谷4178 BZOJ1468 Tree题解点分治

点分治的入门练习。

题目链接 

BZOJ的链接(权限题)

关于点分治的思想我就不再重复了,这里重点说一下如何判重。

我们来看上图,假设我们去除了1节点,求出d[2]=1,d[3]=d[4]=2

假设k为5,这样我们会认为节点(2,3)(2,4)(3,4)的距离小于k,从而累计到答案中

但是我们以2为root做点分治时还会将(3,4)计算一遍,这样就重复了

所以我们每一次计算答案时还要讲所有多余情况减去,最终答案才是我们要求的答案

不难发现多余情况是在root节点与root子节点重复统计的,我们在点分治时将所有root子节点的答案减去就好了

# include<iostream>
# include<cstdio>
# include<algorithm>
# include<set>
# include<cmath>
using namespace std;
const int mn = 40005;
struct edge{int to,next,dis;};
edge e[mn*2];
int head[mn],edge_max;
void add(int x,int y,int z)
{
    e[++edge_max].to=y;
    e[edge_max].dis=z;
    e[edge_max].next=head[x];
    head[x]=edge_max;
}
bool vis[mn];
int n,k,ans;
int mx[mn],siz[mn],root,sum;
int d[mn],deep[mn];
void getroot(int x,int fa)
{
    siz[x]=1,mx[x]=0;
    for(int i=head[x];i;i=e[i].next)
    {
        int u=e[i].to;
        if(u==fa || vis[u])
            continue;
        getroot(u,x);
        siz[x]+=siz[u];
        mx[x]=max(mx[x],siz[u]);
    }
    mx[x]=max(mx[x],sum-siz[x]);
    if(mx[root]>mx[x])
        root=x;
}
void getdeep(int x,int fa)
{
    deep[++deep[0]]=d[x];
    for(int i=head[x];i;i=e[i].next)
    {
        if(e[i].to==fa || vis[e[i].to])
            continue;
        d[e[i].to]=d[x]+e[i].dis;
        getdeep(e[i].to,x);
    }
}
int cal(int x,int now)
{
    d[x]=now,deep[0]=0;
    getdeep(x,0);
    sort(deep+1,deep+1+deep[0]);
    int t=0,l,r;
    for(int l=1,r=deep[0];l<r;)
    {
        if(deep[l]+deep[r]<=k)
        {
            t+=r-l;
            l++;
        }
        else r--;
    }
    return t;
}
void solve(int x)
{
    vis[x]=1;
    ans+=cal(x,0);
    for(int i=head[x];i;i=e[i].next)
    {
          if(vis[e[i].to])
            continue;
          ans-=cal(e[i].to,e[i].dis);
          sum=siz[e[i].to];
          root=0;
          getroot(e[i].to,root);
          solve(root);
    }
}
int main()
{
    int x,y,z;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z),add(y,x,z);
    }
    scanf("%d",&k);
    mx[0]=1<<30,sum=n;
    getroot(1,0);
    solve(root);
    printf("%d\n",ans);
    return 0;
}

 

 

 

posted @ 2018-05-07 16:05  logeadd  阅读(138)  评论(0编辑  收藏  举报