bzoj2152

点分治

按重心分治,每次暴力统计x下所有子树和的答案,分治进子树的时候减去该子树中的贡献即可

复杂度:nlogn

/**************************************************************

    Problem: 2152

    User: syh0313

    Language: C++

    Result: Accepted

    Time:556 ms

    Memory:3124 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <algorithm>

using namespace std;

const int maxn=40010;

int n,st[maxn],to[maxn],nt[maxn],w[maxn],topt;

int si[maxn],mi,rt,num,nd[5],now,dis[maxn],rem[5];

long long a,b;

bool f[maxn];

inline void add(int x,int y,int z)

{to[++topt]=y; nt[topt]=st[x]; st[x]=topt; w[topt]=z;}

void getrt(int x,int fa)

{

    si[x]=1; int p=st[x],ma=0;

    while (p)

    {

        if (!f[to[p]] && to[p]!=fa)

        {

            getrt(to[p],x);

            si[x]+=si[to[p]];

            if (si[to[p]]>ma) ma=si[to[p]];

        }

        p=nt[p];

    }

    if (now-si[x]>ma) ma=now-si[x];

    if (ma<mi) {mi=ma; rt=x;}

}

void getdis(int x,int fa)

{

    nd[dis[x]%3]++; int p=st[x];

    while (p)

    {

        if (!f[to[p]] && to[p]!=fa) {dis[to[p]]=(dis[x]+w[p])%3; getdis(to[p],x);}

        p=nt[p];

    }

}

void solve(int x)

{

    f[x]=1; int p=st[x]; nd[0]=nd[1]=nd[2]=0; nd[0]++;

    while (p)

    {

        if (!f[to[p]]) {dis[to[p]]=w[p]; getdis(to[p],x);}  

        p=nt[p];

    }

    p=st[x]; a+=1ll*nd[0]*(nd[0]-1)/2; a+=1ll*nd[1]*nd[2];

    while (p)

    {

        if (!f[to[p]])

        {

            dis[to[p]]=w[p]; nd[0]=nd[1]=nd[2]=0; getdis(to[p],x);

            a-=1ll*nd[0]*(nd[0]-1)/2; a-=1ll*nd[1]*nd[2];

            mi=1e9; now=si[to[p]]; getrt(to[p],x); solve(rt);

        }

        p=nt[p];

    }

}

int main()

{

    scanf("%d",&n); b=n*n;

    for (int i=1;i<n;i++)

    {

        int x,y,z; scanf("%d%d%d",&x,&y,&z);

        add(x,y,z%3); add(y,x,z%3);

    }

    mi=1e9; now=n; getrt(1,0); solve(rt);  a*=2ll; a+=n;

    printf("%lld/%lld",a/__gcd(a,b),b/__gcd(a,b));

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值