[BZOJ 2152]聪聪可可
Description
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
Input
输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。
Output
以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。
Sample Input
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
Sample Output
13/25
HINT
【样例说明】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。
两种做法:
1.树形dp
f[i][0/1/2]表示以i为根当前已处理过的子树中路径长度为0/1/2的条数
我们令flag[0/1/2]为处理u的某个子树时该子树中以u为根路径长度mod 3为0/1/2的条数。
此时我们让
ans += flag[0]*f[u][0] + flag[1]*f[u][2] + flag[2]*f[u][1];
此后,将flag累加到f[u]上。
最后记得再单独加上f[u][0],表示u为路径端点的情况。
最后对于答案的处理记得∗2(无序点对变有序点对),再加上n(未考虑(i,i)这样的情况)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 struct Node 7 { 8 int next,to,dis; 9 }edge[400001]; 10 int f[200001][3],num,head[200001],n,ans; 11 void add(int u,int v,int dis) 12 { 13 num++; 14 edge[num].next=head[u]; 15 head[u]=num; 16 edge[num].to=v; 17 edge[num].dis=dis; 18 } 19 int gcd(int a,int b) 20 { 21 if (b==0) return a; 22 return gcd(b,a%b); 23 } 24 void dfs_dp(int x,int pa) 25 {int i; 26 for (i=head[x]; i; i=edge[i].next) 27 { 28 int v=edge[i].to; 29 if (v==pa) continue; 30 dfs_dp(v,x); 31 int flag[3]={0}; 32 flag[(edge[i].dis)%3]=f[v][0]; 33 flag[(1+edge[i].dis)%3]=f[v][1]; 34 flag[(2+edge[i].dis)%3]=f[v][2]; 35 ans+=flag[0]*f[x][0]+flag[1]*f[x][2]+flag[2]*f[x][1]; 36 f[x][0]+=flag[0]; 37 f[x][1]+=flag[1]; 38 f[x][2]+=flag[2]; 39 } 40 ans+=f[x][0]; 41 f[x][0]++; 42 } 43 int main() 44 {int i,u,v,d; 45 cin>>n; 46 for (i=1; i<=n-1; i++) 47 { 48 scanf("%d%d%d",&u,&v,&d); 49 add(u,v,d); 50 add(v,u,d); 51 } 52 dfs_dp(1,0); 53 d=gcd(ans*2+n,n*n); 54 printf("%d/%d",(ans*2+n)/d,n*n/d); 55 }
2.点分治:
这道题正解是点分治...
同样还是之前的套路,我们找到重心后,每次只处理与重心有关的路径。
每次找到重心,统计以重心为根的子树中路径长度个数;
同样我们令f[u][0/1/2]表示以u为根路径长度mod 3为0/1/2的条数。
其它与上面差不多
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 struct Node 7 { 8 int next,to,dis; 9 }edge[400001]; 10 int num,head[200001],f[200001][3],ans,k; 11 int size[200001],maxsize[200001],minsize,root,n; 12 bool vis[200001]; 13 void add(int u,int v,int dis) 14 { 15 num++; 16 edge[num].next=head[u]; 17 head[u]=num; 18 edge[num].to=v; 19 edge[num].dis=dis; 20 } 21 int gcd(int a,int b) 22 { 23 if (b==0) return a; 24 return gcd(b,a%b); 25 } 26 void get_size(int x,int fa) 27 { 28 int i; 29 size[x]=1; 30 maxsize[x]=0; 31 for (i=head[x]; i; i=edge[i].next) 32 { 33 int v=edge[i].to; 34 if (vis[v]==0&&v!=fa) 35 { 36 get_size(v,x); 37 size[x]+=size[v]; 38 maxsize[x]=max(maxsize[x],size[v]); 39 } 40 } 41 } 42 void get_root(int r,int x,int fa) 43 { 44 int i; 45 maxsize[x]=max(maxsize[x],size[r]-size[x]); 46 if (maxsize[x]<minsize) 47 { 48 root=x; 49 minsize=maxsize[x]; 50 } 51 for (i=head[x]; i; i=edge[i].next) 52 { 53 int v=edge[i].to; 54 if (vis[v]==0&&v!=fa) 55 { 56 get_root(r,v,x); 57 } 58 } 59 } 60 void get_ans(int x,int fa) 61 { 62 int i; 63 f[x][0]=1; 64 f[x][1]=f[x][2]=0; 65 for (i=head[x]; i; i=edge[i].next) 66 { 67 int v=edge[i].to; 68 if (vis[v]==0&&v!=fa) 69 { 70 get_ans(v,x); 71 f[x][edge[i].dis%3] += f[edge[i].to][0]; 72 f[x][(1+edge[i].dis)%3] += f[edge[i].to][1]; 73 f[x][(2+edge[i].dis)%3] += f[edge[i].to][2]; 74 } 75 } 76 } 77 void solve(int x) 78 { 79 int i; 80 minsize=2e9; 81 get_size(x,0); 82 get_root(x,x,0); 83 vis[root]=1; 84 f[root][0]=1; 85 f[root][1]=f[root][2]=0; 86 for (i=head[root]; i; i=edge[i].next) 87 { 88 int v=edge[i].to; 89 if (vis[v]==0) 90 { 91 get_ans(v,root); 92 int flag[3]={0}; 93 flag[(edge[i].dis)%3]=f[v][0]; 94 flag[(1+edge[i].dis)%3]=f[v][1]; 95 flag[(2+edge[i].dis)%3]=f[v][2]; 96 ans+=flag[0]*f[root][0]+flag[1]*f[root][2]+flag[2]*f[root][1]; 97 f[root][0]+=flag[0]; 98 f[root][1]+=flag[1]; 99 f[root][2]+=flag[2]; 100 } 101 } 102 for (i=head[root]; i; i=edge[i].next) 103 { 104 int v=edge[i].to; 105 if (vis[v]==0) 106 { 107 solve(v); 108 } 109 } 110 } 111 int main() 112 {int i,u,v,d; 113 cin>>n; 114 for (i=1; i<=n-1; i++) 115 { 116 scanf("%d%d%d",&u,&v,&d); 117 add(u,v,d); 118 add(v,u,d); 119 } 120 solve(1); 121 d=gcd(ans*2+n,n*n); 122 printf("%d/%d",(ans*2+n)/d,n*n/d); 123 }