[luogu 2634]聪聪可可
题目描述
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。
他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画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,f[i][x]表示以i为根的子树,到i的距离%3为x的路径数。
每搜完一个点,就与它的同辈更新一下答案。然后更新一下根节点的f值。
记得最后ans=ans*2+n;因为反过来也是一种答案,并且n个点可以单独成为一个答案,即(n,n)。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; template<typename T>void read(T &x) { x=0;char c=getchar(); for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar())x=x*10+c-'0'; } int head[20001],size,n,ans; struct node { int next,to,dis; }edge[40001]; void putin(int from,int to,int dis) { size++; edge[size].to=to; edge[size].dis=dis; edge[size].next=head[from]; head[from]=size; } int f[20001][3]; void dfs(int r,int fa) { int i; for(i=head[r];i!=-1;i=edge[i].next) { int y=edge[i].to; if(y!=fa) { dfs(y,r); ans+=f[y][2-edge[i].dis%3]*f[r][1]+f[y][(edge[i].dis%3<=1)?(edge[i].dis%3)^1:2]*f[r][2]+f[y][(edge[i].dis%3==0)?0:(edge[i].dis%3)^3]*f[r][0]; f[r][edge[i].dis%3]+=f[y][0]; f[r][(edge[i].dis+1)%3]+=f[y][1]; f[r][(edge[i].dis+2)%3]+=f[y][2]; } } ans+=f[r][0]; f[r][0]++; } int gcd(int a,int b) { if(b==0)return a; else return gcd(b,a%b); } int main() { int i,j; read(n); memset(head,-1,sizeof(head)); for(i=1;i<n;i++) { int from,to,dis; read(from);read(to);read(dis); putin(from,to,dis); putin(to,from,dis); } dfs(1,0); ans=ans*2+n; int k=gcd(ans,n*n); printf("%d/%d",ans/k,n*n/k); return 0; }
点分治代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 using namespace std; 8 int n,m; 9 struct node{ 10 int next,to,dis; 11 }edge[40005]; 12 int head[20005],size=0; 13 void putin(int from,int to,int dis){ 14 size++; 15 edge[size].to=to; 16 edge[size].dis=dis; 17 edge[size].next=head[from]; 18 head[from]=size; 19 } 20 int root,sum,son[20005],cnt[20005],vis[20005]; 21 void getroot(int r,int fa){ 22 int i; 23 cnt[r]=1; 24 son[r]=0; 25 for(i=head[r];i!=-1;i=edge[i].next){ 26 int y=edge[i].to; 27 if(y!=fa&&!vis[y]){ 28 getroot(y,r); 29 cnt[r]+=cnt[y]; 30 if(cnt[y]>son[r])son[r]=cnt[y]; 31 } 32 } 33 son[r]=max(son[r],sum-cnt[r]); 34 if(son[r]<son[root])root=r; 35 } 36 int ans,dist[20005],t[3]; 37 void getdeep(int r,int fa){ 38 int i; 39 t[dist[r]]++; 40 for(i=head[r];i!=-1;i=edge[i].next){ 41 int y=edge[i].to; 42 if(y!=fa&&!vis[y]){ 43 dist[y]=(dist[r]+edge[i].dis)%3; 44 getdeep(y,r); 45 } 46 } 47 } 48 int getans(int r,int len){ 49 t[0]=t[1]=t[2]=0; 50 dist[r]=len%3; 51 getdeep(r,0); 52 return t[1]*t[2]*2+t[0]*t[0]; 53 } 54 void solve(int r){ 55 ans+=getans(r,0); 56 vis[r]=1; 57 for(int i=head[r];i!=-1;i=edge[i].next){ 58 int y=edge[i].to; 59 if(!vis[y]){ 60 ans-=getans(y,edge[i].dis); 61 root=0; 62 sum=cnt[y]; 63 getroot(y,0); 64 solve(root); 65 } 66 } 67 } 68 int gcd(int a,int b){ 69 if(b==0)return a; 70 else return gcd(b,a%b); 71 } 72 int main(){ 73 int i,j; 74 memset(head,-1,sizeof(head)); 75 scanf("%d",&n); 76 for(i=1;i<n;i++){ 77 int a,b,c; 78 scanf("%d%d%d",&a,&b,&c); 79 c%=3; 80 putin(a,b,c); 81 putin(b,a,c); 82 } 83 root=0; 84 son[0]=sum=n; 85 getroot(1,0); 86 solve(root); 87 int t=gcd(ans,n*n); 88 printf("%d/%d\n",ans/t,n*n/t); 89 return 0; 90 }