首页 > 技术文章 > [luogu 2634]聪聪可可

huangdalaofighting 2017-08-21 21:07 原文

题目描述

聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。

他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画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 }

 

推荐阅读