BZOJ2152[国家集训队]聪聪可可——点分治
题目描述
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
输入
输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。
输出
以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。
样例输入
5
1 2 1
1 3 2
1 4 1
2 5 3
1 2 1
1 3 2
1 4 1
2 5 3
样例输出
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。
【样例说明】
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=0,%3=1,%3=2的方案数,再减掉同一棵子树中的方案数即可。因为一对点反过来算另一种方案且节点和自己也计入答案,所以答案要x2。注意树的大小及最大子树大小初始值要赋成n。
最后附上代码。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,k; int tot; int num; int ans; int sum; int root; int x,y,z; int s[4]; int to[60000]; int mx[30000]; int val[60000]; int head[30000]; int next[60000]; int size[30000]; bool vis[30000]; int gcd(int a,int b) { if(b!=0) { return gcd(b,a%b); } else { return a; } } void add(int x,int y,int v) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; val[tot]=v; tot++; next[tot]=head[y]; head[y]=tot; to[tot]=x; val[tot]=v; } void getroot(int x,int fa) { size[x]=1; mx[x]=0; for(int i=head[x];i;i=next[i]) { if(to[i]!=fa&&!vis[to[i]]) { getroot(to[i],x); size[x]+=size[to[i]]; mx[x]=max(mx[x],size[to[i]]); } } mx[x]=max(mx[x],num-size[x]); if(!root||mx[x]<mx[root]) { root=x; } } void dfs(int x,int fa,int dis) { s[dis%3]++; for(int i=head[x];i;i=next[i]) { if(to[i]!=fa&&!vis[to[i]]) { dfs(to[i],x,dis+val[i]); } } } int calc(int x,int v) { int ans=0; s[0]=s[1]=s[2]=0; dfs(x,0,v); return s[1]*s[2]*2+s[0]*s[0]; } void partition(int x) { vis[x]=1; ans+=calc(x,0); for(int i=head[x];i;i=next[i]) { if(!vis[to[i]]) { ans-=calc(to[i],val[i]); num=size[to[i]]; root=0; getroot(to[i],0); partition(root); } } } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z%3); } mx[0]=num=n; getroot(1,0); partition(root); sum=n*n; int g=gcd(ans,sum); ans/=g; sum/=g; printf("%d/%d",ans,sum); }