聪聪可可 --- 点分治

传送门:洛谷P2634


题目描述

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

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

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


分析

  点分治的板子就不说了(参考tree),主要讲讲calc
  由于本题要求的是路径权值为3的倍数的数量.对此,可以将路径长度全部%3,然后用数组统计数量即可,例如dis[0],dis[1],dis[2],分别表示余数为0/1/2的路径数量,则
    路径权值为3的倍数的数量 = dis[0] * dis[0] + dis[1] * dis[2] * 2
    ( 1–>2; 2–>1 算两条路径;1–>1也算一条路径 )

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>

#define IL inline
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout);

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

struct Edge
{
    int to, nxt, w;
    IL Edge(int to_ = 0, int nxt_ = 0, int w_ = 0)
    {
        to = to_; nxt = nxt_; w = w_;
    }
}edge[40005];
int cnt;
int last[20005];
IL void add(int u, int v, int w)
{
    w %= 3;
    edge[++cnt] = Edge(v, last[u], w); last[u] = cnt;
    edge[++cnt] = Edge(u, last[v], w); last[v] = cnt;
}

int size[20005];
bool use[20005];
int dis[3];

IL void get_root(int u, int pre, int &root, int &k, int sz)
{
    size[u] = 1;
    int tmp = 0;
    for(int i = last[u], v; i ; i = edge[i].nxt)
    if(edge[i].to != pre && !use[edge[i].to])
    {
        v = edge[i].to;
        get_root(v, u, root, k, sz);
        size[u] += size[v];
        if(size[v] > tmp) tmp = size[v];
    }
    if(sz - size[u] > tmp) tmp = sz - size[u];
    if(tmp < k) { k = tmp; root = u; } 
}

IL void get_dis(int u, int pre, int d)
{
    ++dis[d];
    for(int i = last[u]; i; i = edge[i].nxt)
    if(edge[i].to != pre && !use[edge[i].to])
        get_dis(edge[i].to, u, (d + edge[i].w) % 3);
}

IL int calc(int u, int d)
{
    dis[0] = dis[1] = dis[2] = 0;
    get_dis(u, 0, d);
    return dis[0] * dis[0] + (dis[1] * dis[2] << 1);
}

IL int solve(int p, int sz)
{
    int root = -1, k = sz;
    get_root(p, 0, root, k, sz);
    use[root] = 1;

    int sum = calc(root, 0);

    for(int i = last[root], v; i; i = edge[i].nxt)
    if(!use[edge[i].to])
    {
        v = edge[i].to;
        sum -= calc(v, edge[i].w);
        sum += solve(v, size[v]);
    }

    return sum;
}

IL int gcd(int x, int y) { return !y ? x : gcd(y, x % y);}

int main()
{
    open("2634")

    int n = read();
    for(int i = 1, x, y; i < n; ++i)
    {
        x = read(); y = read();
        add(x, y, read());
    }
    int ans1 = solve(1, n), ans2 = n * n;
    if(!ans1) printf("0"); else
    {
        int g = gcd(ans2, ans1);
        printf("%d/%d\n", ans1 / g, ans2 / g);
    }

    close
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_27121257/article/details/81233313