bzoj1468: Tree

来源:互联网 发布:淘宝直播流量来源 编辑:程序博客网 时间:2024/05/22 08:54

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=1468

题解

  点分治裸题。
  说下过程,就是你每次找重心然后以重心为根进行建树,算出所有点到根的dist,扔到一个表里,两个指针扫一遍得出结果。然后分治下去,在找重心之前应该先dfs一遍把当前子树里的dist扔进表里,扫一遍把结果在ans中减去。
  重心就是到所有点的距离之和最小的点。

代码

//点分治 #include <cstdio>#include <algorithm>#define maxn 40010#define forp for(int p=head[pos];p;p=nex[p])if(to[p]^pre and !grey[to[p]])using namespace std;int N, K, head[maxn], to[maxn<<1], nex[maxn<<1], w[maxn<<1], dist[maxn], G, list[maxn],    tot, size[maxn], sumG, ans;bool grey[maxn];inline void adde(int a, int b, int v){to[++tot]=b;w[tot]=v;nex[tot]=head[a];head[a]=tot;}inline int calc(){    int cnt=0, l=1, r=*list;    sort(list+1,list+*list+1);    while(l<r)    {        while(l<r and list[l]+list[r]>K)r--;        while(l<r and list[l]+list[r]<=K)cnt+=r-l, l++;    }    return cnt;}void dfs_jian(int pos, int pre){    list[++*list]=dist[pos];    for(int p=head[pos];p;p=nex[p])if(to[p]^pre and !grey[to[p]])dfs_jian(to[p],pos);}void calc_dist(int pos, int pre){    list[++*list]=dist[pos];    forp dist[to[p]]=dist[pos]+w[p], calc_dist(to[p],pos);}int calc_size(int pos, int pre){    size[pos]=1;    forp size[pos]+=calc_size(to[p],pos);    return size[pos];}void findG(int pos, int pre, int sum){    if(sum<sumG)G=pos, sumG=sum;    int x=1;    forp x+=size[to[p]];    forp findG(to[p],pos,sum+(x-size[to[p]])*w[p]-size[to[p]]*w[p]);}void solve(int pos){    int x, i;    if(G)*list=0, dfs_jian(pos,-1), ans-=calc();    *list=0, calc_dist(pos,-1);    calc_size(pos,-1);    for(i=1,x=0;i<=*list;i++)x+=list[i];    G=pos, findG(pos,-1,sumG=x);    grey[G]=1, dist[G]=0;    *list=0, calc_dist(G,-1);    ans+=calc();    for(int p=head[G];p;p=nex[p])if(!grey[to[p]])solve(to[p]);}void init(){    int a, b, v, i;    scanf("%d",&N);    for(i=1;i<N;i++)scanf("%d%d%d",&a,&b,&v), adde(a,b,v), adde(b,a,v);    scanf("%d",&K);}int main(){    init();    solve(1);    printf("%d",ans);    return 0;}
0 0
原创粉丝点击