1. 程式人生 > >聰聰可可[點分治]

聰聰可可[點分治]

題目描述

聰聰和可可是兄弟倆,他們倆經常為了一些瑣事打起來,例如家中只剩下最後一根冰棍而兩人都想吃、兩個人都想玩兒電腦(可是他們家只有一臺電腦)……遇到這種問題,一般情況下石頭剪刀布就好了,可是他們已經玩兒膩了這種低智商的遊戲。

他們的爸爸快被他們的爭吵煩死了,所以他發明了一個新遊戲:由爸爸在紙上畫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。


先找樹的重心,求經過他的路徑倍數為3的條數

然後遞迴找兒子,再在兒子的子樹中找重心

如何求條數?我們維護cnt[0],cnt[1],cnt[2] 表示到根路徑長度%3的點的個數

答案就是 cnt[1]*cnt[2]*2 + cnt[0]*cnt[0]


#include<bits/stdc++.h>
#define N 40005
using namespace std;
int first[N],next[N],to[N],w[N],tot;
int n,rt,cnt[5],dis[N],size[N],Maxson[N];
int ans,siz,vis[N];
int read(){
	int cnt=0;char ch=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
	return cnt;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
void add(int x,int y,int z){
	next[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}
void get_root(int u,int f){
	size[u]=1,Maxson[u]=0; 
	for(int i=first[u];i;i=next[i]){
		int t=to[i]; if(t==f||vis[t]) continue;
		get_root(t,u); size[u] += size[t];
		Maxson[u] = max(Maxson[u] , size[t]);
	}
	Maxson[u] = max(Maxson[u] , siz-Maxson[u]);
	if(Maxson[rt] > Maxson[u]) rt = u;
}
void get_dis(int u,int f){
	cnt[dis[u]%3]++;
	for(int i=first[u];i;i=next[i]){
		int t=to[i]; if(t==f||vis[t]) continue;
		dis[t] = dis[u] + w[i];
		get_dis(t,u);
	}
}
int calc(int x,int d){
	cnt[0] = cnt[1] = cnt[2] = 0;
	dis[x] = d;
	get_dis(x,0);
	return cnt[1] * cnt[2] *2 + cnt[0] * cnt[0];
}
void solve(int x){
	ans += calc(x,0);
	vis[x]=1;
	for(int i=first[x];i;i=next[i]){
		int t=to[i]; 
		if(vis[t]) continue;
		ans -= calc(t,w[i]);
		rt = 0 , siz = size[t];
		get_root(t,0);
		solve(rt);
	}
}
int main(){
	n=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read(),z=read()%3;
		add(x,y,z),add(y,x,z); 
	}
	Maxson[0] = siz = n;
	get_root(1,0);
	solve(rt); 
	int x = n * n , g = gcd(x,ans);
	printf("%d/%d\n",ans/g,x/g);
	return 0;
}