【bzoj 1468】Tree(树的点分治)

来源:互联网 发布:怎么查找sql服务器列表 编辑:程序博客网 时间:2024/05/16 00:41

传送门biu~
另一种点分治的写法。求出所有路径,再用容斥减去在同一棵子树中的路径。

#include<bits/stdc++.h>using namespace std;int n,k,center,len,ans;int fa[40005],siz[40005],s[40005],d[40005];int head[40005],nex[80005],to[80005],val[80005],tp;bool b[40005];inline void add(int x,int y,int z){    nex[++tp]=head[x];    head[x]=tp;    to[tp]=y;    val[tp]=z;}void get_tree_center(int x,int father,int tot){    fa[x]=father;    siz[x]=1;    bool flag=true;    for(int i=head[x];i;i=nex[i]){        if(to[i]==father || b[to[i]])   continue;        get_tree_center(to[i],x,tot);        if(siz[to[i]]>tot/2)    flag=false;        siz[x]+=siz[to[i]];    }    if(tot-siz[x]>tot/2)    flag=false;    if(flag)    center=x;}void dfs(int x,int father){    s[++len]=d[x];    for(int i=head[x];i;i=nex[i]){        if(to[i]==father || b[to[i]])   continue;        d[to[i]]=d[x]+val[i];        dfs(to[i],x);    }}int calc(int x,int v){    d[x]=v;len=0;    dfs(x,0);    sort(s+1,s+len+1);    int R=len,re=0;    for(int i=1;i<=len;++i){        while(s[R]+s[i]>k && R>i)   --R;        if(R<=i)    break;        re+=R-i;    }    return re;}void tree_divide(int x){    int tot=siz[x];    get_tree_center(x,0,tot);    x=center;    if(fa[x])   siz[fa[x]]=tot-siz[x];    ans+=calc(x,0);    b[x]=1;    for(int i=head[x];i;i=nex[i]){        if(b[to[i]])    continue;        ans-=calc(to[i],val[i]);        tree_divide(to[i]);    }}int main(){    scanf("%d",&n);    for(int i=1;i<n;++i){        int x,y,z;        scanf("%d%d%d",&x,&y,&z);        add(x,y,z); add(y,x,z);    }    scanf("%d",&k);    siz[1]=n;    tree_divide(1);     printf("%d",ans);    return 0;}