嘘~ 正在从服务器偷取页面 . . .

洛谷P2634 [国家集训队] 聪聪可可 题解


洛谷P2634 [国家集训队] 聪聪可可 题解

题目链接:P2634 [国家集训队] 聪聪可可

题意

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

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

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

输入格式

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

输出格式

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

数据范围

对于 \(100\%\) 的数据,\(n\leq 2 \times 10^4\)

本题从各方面来说还是比较简单的,就是点分治板子而已。

我们可需要统计所有满足条件的情况,然后除以 \(n^2\) 就是答案概率了(记得约分)

统计的方式类似于 P4178 Tree (也是点分治板子),需要去掉不合法的路径。

时间复杂度 \(\mathcal{O}(n \log n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(4e4 + 15))

int n, m, rt, rt_fa, res, sum, pos = 1;
int head[N], sz[N], mx[N], q[N], dis[N], ans[3]; char vis[N];
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
struct Edge { int u, v, w, next; } e[N * 2];
void addEdge(int u, int v, int w) {
    e[++pos] = {u, v, w, head[u]}; head[u] = pos;  
}
void getroot(int u, int fa)
{
    mx[u] = 0; sz[u] = 1;
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].v; if(v == fa || vis[v]) continue;
        getroot(v, u); sz[u] += sz[v]; up(mx[u], sz[v]);
    }
    up(mx[u], sum - sz[u]); if(mx[u] < mx[rt]) { rt = u, rt_fa = fa; }
}
void getdis(int u, int fa)
{
    ++ans[dis[u]];
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].v; if(v == fa || vis[v]) continue;
        dis[v] = (dis[u] + e[i].w) % 3; getdis(v, u);
    }
}
int calc(int u, int w)
{
    memset(ans, 0, sizeof(ans));
    dis[u] = w; getdis(u, 0);
    return ans[2] * ans[1] * 2 + ans[0] * ans[0];
}
void solve(int u)
{
    vis[u] = true; res += calc(u, 0);
    for(int i = head[u]; i; i = e[i].next)
    {
        int v = e[i].v; if(vis[v]) continue;
        res -= calc(v, e[i].w % 3);
        sum = v == rt_fa ? n - sz[u] : sz[v];
        mx[rt = rt_fa = 0] = n; getroot(v, u); solve(rt);
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n; 
    for(int i = 1, u, v, w; i < n; i++)
    {
        cin >> u >> v >> w;
        addEdge(u, v, w); addEdge(v, u, w);
    }
    mx[rt = rt_fa = 0] = sum = n;
    getroot(1, 0); solve(rt);
    int g = gcd(res, n * n);
    cout << res / g << '/' << n * n / g << '\n';
    return 0;
}

题外话

聪聪和可可不是一只猫和一只老鼠吗?洛谷P4206 [NOI2005] 聪聪与可可 题解


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录