【题目】
Description
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。
他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画 n n n 个“点”,并用 n − 1 n-1 n−1 条“边”把这 n n n 个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是 3 3 3 的倍数,则判聪聪赢,否则可可赢。
聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
Input
输入的第 1 1 1 行包含 1 1 1 个正整数 n n n。后面 n − 1 n-1 n−1 行,每行 3 3 3 个整数 x x x、 y y y、 w w w,表示 x x x 号点和 y y y 号点之间有一条边,上面的数是 w w w。
Output
以即约分数形式输出这个概率(即 “ a / b ” “a/b” “a/b” 的形式,其中 a a a 和 b b b 必须互质。如果概率为 1 1 1,输出 “ 1 / 1 ” “1/1” “1/1”)。
Sample Input
5
1 2 1
1 3 2
1 4 1
2 5 3
Sample Output
13/25
Hint
【样例说明】
13
13
13 组点对分别是
(
1
,
1
)
(
2
,
2
)
(
2
,
3
)
(
2
,
5
)
(
3
,
2
)
(
3
,
3
)
(
3
,
4
)
(
3
,
5
)
(
4
,
3
)
(
4
,
4
)
(
5
,
2
)
(
5
,
3
)
(
5
,
5
)
(1,1) (2,2) (2,3) (2,5) (3,2) (3,3) (3,4) (3,5) (4,3) (4,4) (5,2) (5,3) (5,5)
(1,1)(2,2)(2,3)(2,5)(3,2)(3,3)(3,4)(3,5)(4,3)(4,4)(5,2)(5,3)(5,5)。
【数据规模】
对于
100
%
100\%
100% 的数据,
n
n
n ≤
20000
20000
20000。
【分析】
题解:点分治模板题
其实这道题和我之前写的一道题很像戳这里
不同的地方就是统计答案时,我们统计一下每个点到重心的距离模 3 3 3 后的值出现的次数,即用 s u m i sum_i sumi 就表示到重心路径模 3 3 3 为 i i i 的点的总数(当然 i i i 只可能为 0 0 0, 1 1 1 或者 2 2 2)
那么总共的 a n s = s u m 0 ∗ s u m 0 + 2 ∗ s u m 1 ∗ s u m 2 ans=sum_0*sum_0+2*sum_1*sum_2 ans=sum0∗sum0+2∗sum1∗sum2
后面的 s u m 1 ∗ s u m 2 sum_1*sum_2 sum1∗sum2 还要乘 2 2 2 的原因是顺序不同的点对是不同的(比如 ( 2 , 3 ) , ( 3 , 2 ) (2,3),(3,2) (2,3),(3,2) 是不同的)
当然由于重复计算,在递归到子树时,还要将部分答案减掉
而总情况显然是
n
2
n^2
n2,因此把
a
n
s
n
2
\frac{ans}{n^2}
n2ans 化简一下就可以了
【代码】
#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;
}