• [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 }
  • 相关阅读:
    【luogu P1343 地震逃生】 题解
    【luogu P3931 SAC E#1
    【luogu P3275 [SCOI2011]糖果】 题解
    【luogu P2947 [USACO09MAR]向右看齐Look Up】 题解
    【luogu P1456 Monkey King】 题解
    【luogu P3377 左偏树(可并堆)】 模板
    【luogu P1993 小K的农场】 题解
    Sqlmap注入Base64编码的注入点
    kali高速更新源以及主题修改方法
    DiscuzX3.1搬家全过程
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/7599404.html
Copyright © 2020-2023  润新知