zoukankan      html  css  js  c++  java
  • [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
    ",ans/t,n*n/t);
    89   return 0;
    90 }
  • 相关阅读:
    项目中的*签到*小功能!
    亲们,拿到DateTime.Now你是否也是这样比较的?
    <input type="file" />,美化自定义上传按钮
    让你的页面实现自定义的 Ajax Loading加载的体验!
    按回车键提交表单!
    字符串比较大小,CompareTo来搞定!
    巧用Contains可以做到过滤同类项!
    项目开发中遇到的Bug知识整理!
    SharePoint中详细的版本对比
    ASP.NET安全隐患及SharePoint中的Workaround
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/7406688.html
Copyright © 2011-2022 走看看