[BZOJ 2152] 聪聪可可

Link:

BZOJ 2152 传送门

Solution:

直接上点分治就行了(其实也可以树形$dp$)

其中对于穿过重心的边的统计类似于树形$dp$的计算方式:

将当前子树中模3分别为0/1/2的边与之前所有子树产生的边进行组合

Code:

//by NewErA 
#include <bits/stdc++.h>

using namespace std;
typedef pair<int,int> P;
const int INF=1<<27;
const int MAXN=20005;
vector<P> G[MAXN];
int x,y,z,n,sz[MAXN],vis[MAXN],mx_sub[MAXN],v_sum=0,res=0,root;
int dist[MAXN],f[MAXN][5];

void getroot(int x,int anc)
{
    sz[x]=1;mx_sub[x]=0;  //mx_sub要重新初始化
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i].first;
        if(v==anc || vis[v]) continue;
        getroot(v,x);sz[x]+=sz[v];
        mx_sub[x]=max(mx_sub[x],sz[v]);
    }
    mx_sub[x]=max(mx_sub[x],v_sum-sz[x]);
    if(mx_sub[x]<mx_sub[root]) root=x;
}

void cal(int x,int anc,int val,int pos)
{
    f[pos][val%3]++;
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i].first;
        if(v==anc || vis[v]) continue;
        cal(v,x,val+G[x][i].second,pos);
    }
}

void solve(int x)
{
    vis[x]=true;
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i].first;
        if(vis[v]) continue;
        
        int t0=f[x][0],t1=f[x][1],t2=f[x][2];
        cal(v,x,G[x][i].second,x);
        res+=(t0+1)*(f[x][0]-t0)+t2*(f[x][1]-t1)+t1*(f[x][2]-t2);
        
        v_sum=sz[v];getroot(v,root=0);
        solve(root);//注意这里的变量名
    }
}

int GCD(int a,int b){return (a%b)?GCD(b,a%b):b;}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        G[x].push_back(P(y,z%3));G[y].push_back(P(x,z%3));
    }
    mx_sub[0]=v_sum=n,getroot(1,root=0);
    solve(root);res=res*2+n;
    
    int gcd=GCD(res,n*n);
    cout << res/gcd << '/' << n*n/gcd;
    return 0;
}

 

posted @ 2018-07-13 07:12  NewErA  阅读(107)  评论(0编辑  收藏  举报