BZOJ 2152 聪聪可可(点分治)

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2152

【题目大意】

  给出一棵树,问任取两点之间距离为3的倍数的概率是多少

【题解】

  树分治统计模3之后不同的链长计算答案,最后和总数求个gcd化简即可。

【代码】

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring> 
using namespace std;
typedef pair<int,int> P;
#define ff first
#define ss second
const int N=20010;
int n,cnt,ans,root,sum;
int size[N],dp[N],d[N],t[5],vis[N];
vector<P> G[N];
void add_edge(int u,int v,int w){
    G[u].push_back(P(v,w));
    G[v].push_back(P(u,w));
}
void getroot(int x,int fx){
    size[x]=1; dp[x]=0;
    for(int i=0;i<G[x].size();i++){
        int y=G[x][i].ff,z=G[x][i].ss;
        if(!vis[y]&&y!=fx){
            getroot(y,x);
            size[x]+=size[y];
            dp[x]=max(dp[x],size[y]);
        }
    }dp[x]=max(dp[x],sum-size[x]);
    if(dp[x]<dp[root])root=x;
}
void getdeep(int x,int fx){
    t[d[x]]++;
    for(int i=0;i<G[x].size();i++){
        int y=G[x][i].ff,z=G[x][i].ss;
        if(!vis[y]&&y!=fx){
            d[y]=(d[x]+z)%3;
            getdeep(y,x);
        }
    }
}
int cal(int x,int dx){
    t[0]=t[1]=t[2]=0;
    d[x]=dx;
    getdeep(x,0);
    return t[1]*t[2]*2+t[0]*t[0];
}
void solve(int x){
    ans+=cal(x,0); vis[x]=1;
    for(int i=0;i<G[x].size();i++){
        int y=G[x][i].ff,z=G[x][i].ss;
        if(!vis[y]){
            ans-=cal(y,z);
            root=0;sum=size[y];
            getroot(y,0);
            solve(root);
        }
    }
}
int main(){
    while(~scanf("%d",&n)){
    	for(int i=1;i<=n;i++)G[i].clear();
        for(int i=1;i<n;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w%3);
        }dp[0]=sum=n; ans=0;
        memset(vis,0,sizeof(vis));
        getroot(1,0);
        solve(root);
        int t=__gcd(ans,n*n);
        printf("%d/%d
",ans/t,n*n/t);
    }return 0;
}
原文地址:https://www.cnblogs.com/forever97/p/bzoj2152.html