1. 程式人生 > >【BZOJ 2152】聰聰可可

【BZOJ 2152】聰聰可可

【題目】

傳送門

Description

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

他們的爸爸快被他們的爭吵煩死了,所以他發明了一個新遊戲:由爸爸在紙上畫 n n 個“點”,並用 n

1 n-1 條“邊”把這 n n 個“點”恰好連通(其實這就是一棵樹)。並且每條“邊”上都有一個數。接下來由聰聰和可可分別隨即選一個點(當然他們選點時是看不到這棵樹的),如果兩個點之間所有邊上數的和加起來恰好是 3
3
的倍數,則判聰聰贏,否則可可贏。

聰聰非常愛思考問題,在每次遊戲後都會仔細研究這棵樹,希望知道對於這張圖自己的獲勝概率是多少。現請你幫忙求出這個值以驗證聰聰的答案是否正確。

Input

輸入的第 1 1 行包含 1

1 個正整數 n n 。後面 n 1 n-1 行,每行 3 3 個整數 x x y y w w ,表示 x x 號點和 y y 號點之間有一條邊,上面的數是 w w

Output

以即約分數形式輸出這個概率(即 a / b “a/b” 的形式,其中 a a b b 必須互質。如果概率為 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 組點對分別是 ( 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\% 的資料, n n 20000 20000

【分析】

題解:點分治模板題

其實這道題和我之前寫的一道題很像戳這裡

不同的地方就是統計答案時,我們統計一下每個點到重心的距離模 3 3 後的值出現的次數,即用 s u m i sum_i 就表示到重心路徑模 3 3 i i 的點的總數(當然 i i 只可能為 0 0 1 1 或者 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

後面的 s u m 1 s u m 2 sum_1*sum_2 還要乘 2 2 的原因是順序不同的點對是不同的(比如 ( 2 , 3 ) , ( 3 , 2 ) (2,3),(3,2) 是不同的)

當然由於重複計算,在遞迴到子樹時,還要將部分答案減掉

而總情況顯然是 n 2 n^2 ,因此把 a n s n 2 \frac{ans}{n^2} 化簡一下就可以了

【程式碼】

#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;
}