zoukankan      html  css  js  c++  java
  • 【P2634】聪聪可可——点分治

    (题面来自Luogu)

    题目描述

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

    他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。

    聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。

    输入输出格式

    输入格式:

    输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。

    输出格式:

    以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。

      用点分治统计从每个点u出发的路径,在cnt[k]中计数;其中k属于{0, 1, 2},表示该数组统计通过当前节点的长度%3等于k的路径数目。考虑将这些节点两两组合成长度%3等于0的路径:cnt[1]可以与cnt[2]两两组合,且题中给定的是有序点对,那么通过点u的满足条件的路径数ret += (cnt[2] * cnt[1] * 2)。cnt[0]可以两两组合,且可以重复,则ret += cnt[0] * cnt[0]。

    代码:

     
    1. #include <iostream>    
    2. #include <cstdio>    
    3. #include <cstring>    
    4. #include <cctype>    
    5. #define BUG puts("$$$")    
    6. #define maxn 20010    
    7. using namespace std;    
    8. const int inf = 0x3f3f3f3f;    
    9. int n;    
    10. template <typename T>    
    11. void read(T &x) {    
    12.     x = 0;    
    13.     char ch = getchar();    
    14.     int f = 1;    
    15.     while (!isdigit(ch)) {    
    16.         if (ch == '-') f = -1;     
    17.         ch = getchar();    
    18.     }    
    19.     while (isdigit(ch)) {    
    20.         x = x * 10 + (ch ^ 48);    
    21.         ch = getchar();    
    22.     }    
    23.     x *= f;    
    24. }    
    25. long long gcd(long long a, long long b) {    
    26.     if (!b) return a;    
    27.     return gcd(b, a % b);    
    28. }    
    29. int head[maxn], top;    
    30. struct E {    
    31.     int to, nxt, w;    
    32. } edge[maxn << 1];    
    33. inline void insert(int u, int v, int w) {    
    34.     edge[++top] = (E) {v, head[u], w};    
    35.     head[u] = top;    
    36. }    
    37. int cnt[3];    
    38. int size[maxn], Size, mn, root;    
    39. bool vis[maxn];    
    40. void find_rt(int u, int pre) {    
    41.     size[u] = 1;    
    42.     int son = 0;    
    43.     for (int i = head[u]; i; i = edge[i].nxt) {    
    44.         int v = edge[i].to;    
    45.         if (v == pre || vis[v]) continue;    
    46.         find_rt(v, u);    
    47.         size[u] += size[v];    
    48.         if (size[v] > size[son]) son = v;    
    49.     }    
    50.     int cur = max(size[son], Size - size[u]);    
    51.     if (cur < mn)    
    52.         mn = cur, root = u;    
    53. }    
    54. void dfs(int u, int pre, int depth, int val) {    
    55.     cnt[depth % 3] += val;    
    56.     for (int i = head[u]; i; i = edge[i].nxt) {    
    57.         int v = edge[i].to;    
    58.         if (v == pre || vis[v]) continue;    
    59.         dfs(v, u, depth + edge[i].w, val);    
    60.     }    
    61. }    
    62. long long solve(int u, int extra) {    
    63.     dfs(u, 0, extra, 1);    
    64.     long long ret = 1LL * cnt[1] * cnt[2] * 2 + 1LL * cnt[0] * cnt[0];    
    65.     dfs(u, 0, extra, -1);    
    66.     return ret;    
    67. }    
    68. long long ans;    
    69. void divide(int u) {    
    70.     vis[u] = true;    
    71.     ans += solve(u, 0);    
    72.     int curSize = Size;    
    73.     for (int i = head[u]; i; i = edge[i].nxt) {    
    74.         int v = edge[i].to;    
    75.         if (vis[v]) continue;    
    76.         ans -= solve(v, edge[i].w);    
    77.         mn = inf, Size = size[u] > size[v] ? size[v] : curSize - size[u];    
    78.         find_rt(v, u);    
    79.         divide(root);    
    80.     }    
    81. }    
    82. int main() {    
    83.     read(n);    
    84.     int u, v, w;    
    85.     for (int i = 1; i < n; ++i) {    
    86.         read(u), read(v), read(w);    
    87.         insert(u, v, w), insert(v, u, w);    
    88.     }    
    89.     mn = inf, Size = n;    
    90.     find_rt(1, 0);    
    91.     divide(root);    
    92.     long long down = 1LL * n * n, k = gcd(down, ans);    
    93.     printf("%lld/%lld", ans / k, down / k);    
    94.     return 0;    
    95. }    
  • 相关阅读:
    git
    composer
    laravel saas
    算法模板-01背包
    GMP-C/C++(大数库)使用方法
    算法模板-容斥原理
    算法模板-欧拉函数
    算法模板-素数判断/素数筛法
    算法模板-质因数分解
    算法模板-快速幂取模
  • 原文地址:https://www.cnblogs.com/TY02/p/11354856.html
Copyright © 2011-2022 走看看