[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

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 }

 

posted @ 2017-09-26 22:37  Z-Y-Y-S  阅读(300)  评论(0编辑  收藏  举报