zoukankan      html  css  js  c++  java
  • [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。

    题解

    解法一

    考虑树形$DP$解法,我们令$f[u][0/1/2]$表示以$u$为根当前已处理过的子树中路径长度$mod$ $3$为$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 //It is made by Awson on 2017.9.20
     2 #include <set>
     3 #include <map>
     4 #include <cmath>
     5 #include <ctime> 
     6 #include <queue>
     7 #include <stack>
     8 #include <string>
     9 #include <cstdio>
    10 #include <vector>
    11 #include <cstdlib>
    12 #include <cstring>
    13 #include <iostream>
    14 #include <algorithm>
    15 #define Min(a, b) ((a) < (b) ? (a) : (b))
    16 #define Max(a, b) ((a) > (b) ? (a) : (b))
    17 #define LL long long
    18 using namespace std;
    19 const int N = 20000;
    20 const int INF = ~0u>>1;
    21 
    22 int n, u, v, c;
    23 struct tt {
    24     int to, cost, next;
    25 }edge[N*2+5];
    26 int path[N+5], top;
    27 int f[N+5][3];
    28 int ans = 0;
    29 
    30 int gcd(int a, int b) {
    31     return b == 0 ? a : gcd(b, a%b);
    32 }
    33 void add(int u, int v, int c) {
    34     edge[++top].to = v;
    35     edge[top].cost = c;
    36     edge[top].next = path[u];
    37     path[u] = top;
    38 }
    39 void dfs(int u, int fa) {
    40     int flag[3];
    41     for (int i = path[u]; i; i = edge[i].next)
    42         if (edge[i].to != fa) {
    43             dfs(edge[i].to, u);
    44             flag[edge[i].cost%3] = f[edge[i].to][0];
    45             flag[(1+edge[i].cost)%3] = f[edge[i].to][1];
    46             flag[(2+edge[i].cost)%3] = f[edge[i].to][2];
    47             ans += flag[0]*f[u][0] + flag[1]*f[u][2] + flag[2]*f[u][1];
    48             f[u][0] += flag[0];
    49             f[u][1] += flag[1];
    50             f[u][2] += flag[2];
    51         }
    52     ans += f[u][0];
    53     f[u][0]++;
    54 }
    55 void work() {
    56     for (int i = 1; i < n; i++) {
    57         scanf("%d%d%d", &u, &v, &c);
    58         c %= 3;
    59         add(u, v, c); add(v, u, c);
    60     }
    61     dfs(1, 0);
    62     int tmp = gcd(ans*2+n, n*n);
    63     printf("%d/%d
    ", (ans*2+n)/tmp, n*n/tmp);
    64 }
    65 int main() {
    66     while (~scanf("%d", &n))
    67         work();
    68     return 0;
    69 }
    树形DP

    解法二

    这道题正解是点分治...

    同样还是之前的套路,我们找到重心后,每次只处理与重心有关的路径。

    每次找到重心,统计以重心为根的子树中路径长度个数;

    同样我们令$f[u][0/1/2]$表示以$u$为根路径长度$mod$ $3$为$0/1/2$的条数。

    可以用之前的做法统计,但这里给出另一种思路。

    我们先统计所有的子树中节点到根的路径长度个数,用刚刚的方程转移。

    但直接相乘会出现某条路径不是简单路径,简而言之就是两个端点来自同一个子树,

    其实我们只要将来自同一个子树的方案删去即可。

    (因为两种解法一起写的,这里给出的点分治的代码受树形DP的代码思想影响比较大,其实很多程序段是不必要的)

      1 //It is made by Awson on 2017.9.20
      2 #include <set>
      3 #include <map>
      4 #include <cmath>
      5 #include <ctime> 
      6 #include <queue>
      7 #include <stack>
      8 #include <string>
      9 #include <cstdio>
     10 #include <vector>
     11 #include <cstdlib>
     12 #include <cstring>
     13 #include <iostream>
     14 #include <algorithm>
     15 #define Min(a, b) ((a) < (b) ? (a) : (b))
     16 #define Max(a, b) ((a) > (b) ? (a) : (b))
     17 #define LL long long
     18 using namespace std;
     19 const int N = 20000;
     20 const int INF = ~0u>>1;
     21 
     22 struct tt {
     23     int to, cost, next;
     24 }edge[N*2+5];
     25 int path[N+5], top;
     26 int n, u, v, c;
     27 int ans;
     28 bool vis[N+5];
     29 int size[N+5], mx[N+5], minsize, root;
     30 int f[N+5][3];
     31 
     32 int gcd(int a, int b) {
     33     return b == 0 ? a : gcd(b, a%b);
     34 }
     35 void add(int u, int v, int c) {
     36     edge[++top].to = v;
     37     edge[top].cost = c;
     38     edge[top].next = path[u];
     39     path[u] = top;
     40 }
     41 void get_size(int u, int fa) {
     42     size[u] = 1; mx[u] = 0;
     43     for (int i = path[u]; i; i = edge[i].next)
     44         if (!vis[edge[i].to] && edge[i].to != fa) {
     45             get_size(edge[i].to, u);
     46             size[u] += size[edge[i].to];
     47             mx[u] = Max(mx[u], size[edge[i].to]);
     48         }
     49 }
     50 void get_root(int r, int u, int fa) {
     51     mx[u] = Max(mx[u], size[r]-size[u]);
     52     if (mx[u] < minsize) minsize = mx[u], root = u;
     53     for (int i = path[u]; i; i = edge[i].next)
     54         if (!vis[edge[i].to] && edge[i].to != fa)
     55             get_root(r, edge[i].to, u);
     56 }
     57 void get_ans(int u, int fa) {
     58     f[u][0] = 1;
     59     f[u][1] = f[u][2] = 0;
     60     for (int i = path[u]; i; i = edge[i].next)
     61         if (!vis[edge[i].to] && edge[i].to != fa) {
     62             get_ans(edge[i].to, u);
     63             f[u][edge[i].cost%3] += f[edge[i].to][0];
     64             f[u][(1+edge[i].cost)%3] += f[edge[i].to][1];
     65             f[u][(2+edge[i].cost)%3] += f[edge[i].to][2];
     66         }
     67 }
     68 void solve(int x) {
     69     minsize = INF;
     70     int flag[3] = {0};
     71     get_size(x, 0);
     72     get_root(x, x, 0);
     73     vis[root] = true;
     74     f[root][0] = 1;
     75     f[root][1] = f[root][2] = 0;
     76     for (int i = path[root]; i; i = edge[i].next)
     77         if (!vis[edge[i].to]) {
     78             get_ans(edge[i].to, 0);
     79             flag[edge[i].cost%3] = f[edge[i].to][0];
     80             flag[(1+edge[i].cost)%3] = f[edge[i].to][1];
     81             flag[(2+edge[i].cost)%3] = f[edge[i].to][2];
     82             ans -= flag[0]*flag[0] + flag[1]*flag[2]*2;
     83             f[root][0] += flag[0];
     84             f[root][1] += flag[1];
     85             f[root][2] += flag[2];
     86         }
     87     ans += f[root][0]*f[root][0] + f[root][1]*f[root][2]*2;
     88     for (int i = path[root]; i; i = edge[i].next)
     89         if (!vis[edge[i].to])
     90             solve(edge[i].to);
     91 }
     92 void work() {
     93     for (int i = 1; i < n; i++) {
     94         scanf("%d%d%d", &u, &v, &c);
     95         c %= 3;
     96         add(u, v, c); add(v, u, c);
     97     }
     98     ans = 0;
     99     solve(1);
    100     int tmp = gcd(ans, n*n);
    101     printf("%d/%d
    ", ans/tmp, n*n/tmp);
    102 }
    103 int main() {
    104     while (~scanf("%d", &n))
    105         work();
    106     return 0; 
    107 }
    点分治
  • 相关阅读:
    内存相关函数
    Redis入门
    libevent(九)evhttp
    Python基础00 教程
    Python之re模块
    Makefile入门
    cmake安装jsoncpp
    awk调用date命令
    SQLite使用(二)
    SQLite使用(一)
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/7562535.html
Copyright © 2011-2022 走看看