由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。
聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
THUWC晚上无聊来刷blog
今天凉凉
还是这道树分治比较友善
(p.s. 其实直接dfs就可以了)
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 20010
using namespace std;
struct edge{ int v,c,nt; } G[N<<1];
int h[N],d[N],f[N],vis[N],sz[N],n,m,cnt=1,rt,Sz,c[3],ans=0;
inline int gcd(int a,int b){
for(int c;b;a=b,b=c)c=a%b;
return a;
}
inline void adj(int x,int y,int c){
G[++cnt]=(edge){y,c,h[x]}; h[x]=cnt;
G[++cnt]=(edge){x,c,h[y]}; h[y]=cnt;
}
void gSz(int x,int p){
++Sz;
for(int v,i=h[x];i;i=G[i].nt)
if(!vis[v=G[i].v] && v!=p) gSz(v,x);
}
void gRt(int x,int p){
f[x]=0; sz[x]=1;
for(int v,i=h[x];i;i=G[i].nt)
if(!vis[v=G[i].v] && v!=p){
gRt(v,x); sz[x]+=sz[v]; f[x]=max(f[x],sz[v]);
}
f[x]=max(f[x],Sz-sz[x]);
if(f[x]<f[rt]) rt=x;
}
void dfs(int x,int p,int d){
c[d%3]++;
for(int v,i=h[x];i;i=G[i].nt)
if(!vis[v=G[i].v] && v!=p) dfs(v,x,d+G[i].c);
}
int Calc(int x,int d){
*c=c[1]=c[2]=0; dfs(x,0,d);
return c[1]*c[2]*2+*c*(*c-1)+*c;
}
void dgs(int x){
vis[x]=1; ans+=Calc(x,0);
for(int v,i=h[x];i;i=G[i].nt)
if(!vis[v=G[i].v]){
ans-=Calc(v,G[i].c);
Sz=0; gSz(v,0);
f[rt=0]=1<<30;
gRt(v,0);
dgs(rt);
}
}
int main(){
scanf("%d",&n);
for(int x,y,c,i=1;i<n;++i){
scanf("%d%d%d",&x,&y,&c);
adj(x,y,c);
}
Sz=n; *f=1<<30;
gRt(1,0);
dgs(rt);
int t=n*n,r=gcd(t,ans);
printf("%d/%d\n",ans/r,t/r);
}