【题目】
Description
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。
他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画 个“点”,并用 条“边”把这 个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是 的倍数,则判聪聪赢,否则可可赢。
聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
Input
输入的第 行包含 个正整数 。后面 行,每行 个整数 、 、 ,表示 号点和 号点之间有一条边,上面的数是 。
Output
以即约分数形式输出这个概率(即 的形式,其中 和 必须互质。如果概率为 ,输出 )。
Sample Input
5
1 2 1
1 3 2
1 4 1
2 5 3
Sample Output
13/25
Hint
【样例说明】
组点对分别是
。
【数据规模】
对于
的数据,
≤
。
【分析】
题解:点分治模板题
其实这道题和我之前写的一道题很像戳这里
不同的地方就是统计答案时,我们统计一下每个点到重心的距离模 后的值出现的次数,即用 就表示到重心路径模 为 的点的总数(当然 只可能为 , 或者 )
那么总共的
后面的 还要乘 的原因是顺序不同的点对是不同的(比如 是不同的)
当然由于重复计算,在递归到子树时,还要将部分答案减掉
而总情况显然是
,因此把
化简一下就可以了
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
#define inf (1ll<<31ll)-1
using namespace std;
int t,ans,num,root;
int size[N],Max[N],sum[3];
int first[N],v[N],w[N],next[N];
bool vis[N];
void add(int x,int y,int z)
{
t++;
next[t]=first[x];
first[x]=t;
v[t]=y;
w[t]=z;
}
void dfs(int x,int father)
{
int i,j;
Max[x]=0,size[x]=1;
for(i=first[x];i;i=next[i])
{
j=v[i];
if(j!=father&&!vis[j])
{
dfs(j,x);
size[x]+=size[j];
Max[x]=max(Max[x],size[j]);
}
}
}
void find(int rt,int x,int father)
{
int i,j;
Max[x]=max(Max[x],size[rt]-size[x]);
if(num>Max[x]) root=x,num=Max[x];
for(i=first[x];i;i=next[i])
{
j=v[i];
if(j!=father&&!vis[j])
find(rt,j,x);
}
}
void dist(int x,int father,int len)
{
int i,j;
sum[len]++;
for(i=first[x];i;i=next[i])
{
j=v[i];
if(j!=father&&!vis[j])
dist(j,x,(len+w[i])%3);
}
}
int calc(int x,int l)
{
memset(sum,0,sizeof(sum));dist(x,0,l%3);
return sum[0]*sum[0]+2*sum[1]*sum[2];
}
void solve(int x)
{
int i,j;
dfs(x,0);
num=inf,find(x,x,0);
ans+=calc(root,0);
vis[root]=true;
for(i=first[root];i;i=next[i])
{
j=v[i];
if(!vis[j])
{
ans-=calc(j,w[i]);
solve(j);
}
}
}
int main()
{
int n,i,x,y,z;
scanf("%d",&n);
for(i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
solve(1);
int k=__gcd(ans,n*n);
printf("%d/%d",ans/k,n*n/k);
return 0;
}