【BZOJ 2152】聰聰可可
【題目】
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;
}