题目描述
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。

他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。

聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。

输入格式
输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。

输出格式
以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。

输入输出样例
输入 #1复制
5
1 2 1
1 3 2
1 4 1
2 5 3
输出 #1复制
13/25
说明/提示
【样例说明】

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)。

【数据规模】

对于100%的数据,n<=20000。


树形dp或者点分治。

树形dp已经写过类似的了,现在写一个点分治。

这道题目相当于就是要我们求任取两点的路径长度%3为0的个数。

然后总的选法就是 n*n ,因为可以取到同一个点。

然后就点分治了,分为经过根的路径和不经过根的路径。

res+=now[0]*cnt[0]*2+now[1]*cnt[2]*2+now[2]*cnt[1]*2+now[0]*2;

now数组是这一次计算某个子树的答案,cnt数组是之前所有子树的答案。合并即可。

now[0] * 2 就是直接到根的个数。

最后总答案要加上n,因为都选到同一个点也是满足的。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e4+10,inf=0x3f3f3f3f;
int n,rt,mx,sum,sz[N],vis[N],d[N],cnt[3],res,now[3];
int head[N],nex[N<<1],to[N<<1],w[N<<1],tot;
inline void add(int a,int b,int c){to[++tot]=b; nex[tot]=head[a]; w[tot]=c; head[a]=tot;}
void get(int x,int fa){
	sz[x]=1; int t=0;
	for(int i=head[x];i;i=nex[i]){
		if(vis[to[i]]||to[i]==fa)	continue;
		get(to[i],x); sz[x]+=sz[to[i]]; t=max(t,sz[to[i]]);
	}
	t=max(t,sum-sz[x]); if(t<mx) mx=t,rt=x;
}
void getdis(int x,int fa){
	now[d[x]%3]++;
	for(int i=head[x];i;i=nex[i]){
		if(to[i]==fa||vis[to[i]])	continue;
		d[to[i]]=d[x]+w[i]; getdis(to[i],x);
	}
}
void solve(int x){
	for(int i=head[x];i;i=nex[i]){
		if(vis[to[i]])	continue;
		d[to[i]]=w[i];	memset(now,0,sizeof now); getdis(to[i],x);
		res+=now[0]*cnt[0]*2+now[1]*cnt[2]*2+now[2]*cnt[1]*2+now[0]*2;
		for(int j=0;j<3;j++)	cnt[j]+=now[j];
	}
}
void divide(int x){
	vis[x]=1;	memset(cnt,0,sizeof cnt);	solve(x);
	for(int i=head[x];i;i=nex[i]){
		if(vis[to[i]])	continue;
		sum=sz[to[i]]; mx=inf; rt=0; get(to[i],0); get(rt,0);
		divide(rt);
	}
}
signed main(){
	cin>>n;
	for(int i=1,a,b,c;i<n;i++)	scanf("%d %d %d",&a,&b,&c),add(a,b,c),add(b,a,c);
	mx=inf; sum=n; get(1,0); get(rt,0); divide(rt); res+=n;
	int gcd=__gcd(res,n*n);
	cout<<res/gcd<<'/'<<n*n/gcd<<endl;
	return 0;
}