第一次♂点分治
由爸爸♂ljx在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
代码:
#include<iostream>
#include<cstdio>
using namespace std;
struct point
{
int dis;
int to;
int next;
}e[100010];
int n,num,ans,root,sum;
int t[4],head[50010],son[50010],f[50010],d[50010];
bool vis[50010];
int gcd(int a,int b)
{
if(!b) return a;
else return gcd(b,a%b);
}
void add(int from,int to,int dis)
{
e[++num].next=head[from];
e[num].to=to;
e[num].dis=dis;
head[from]=num;
}
void getroot(int x,int fa)
{
son[x]=1;
f[x]=0;
for(int i=head[x];i!=0;i=e[i].next)
{
int to=e[i].to;
if(to==fa||vis[to]) continue;
getroot(to,x);
son[x]+=son[to];
f[x]=max(f[x],son[to]);
}
f[x]=max(f[x],sum-son[x]);
if(f[x]<f[root]) root=x;
}
void getdeep(int x,int fa)
{
t[d[x]]++;
for(int i=head[x];i!=0;i=e[i].next)
{
int to=e[i].to;
if(to==fa||vis[to]) continue;
d[to]=(d[x]+e[i].dis)%3;
getdeep(to,x);
}
}
int cal(int x,int cost)
{
t[0]=t[1]=t[2]=0;
d[x]=cost;
getdeep(x,0);
return t[1]*t[2]*2+t[0]*t[0];
}
void solve(int x)
{
ans+=cal(x,0);
vis[x]=true;
for(int i=head[x];i!=0;i=e[i].next)
{
int to=e[i].to;
if(vis[to]) continue;
ans-=cal(to,e[i].dis);
root=0;
sum=son[to];
getroot(to,0);
solve(root);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
z%=3;
add(x,y,z);
add(y,x,z);
}
f[0]=sum=n;
getroot(1,0);
solve(root);
int t=gcd(ans,n*n);
printf("%d/%d\n",ans/t,n*n/t);
return 0;
}