【算法练习】Luogu 2634 [国家集训队]聪聪可可(点分治)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pengwill97/article/details/82261237

题意

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

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

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

题解

不能像点分治模板题那样做了,因为会被卡掉。
题目要求计算%3 = 0的边数,所以只需要记录一下模数为012的即可了。

代码

// luogu-judger-enable-o2
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 2e5+1;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
struct edge {
    int to, nxt;
    int w;
}e[nmax << 1];
int head[nmax], tot;
int n, m;
void add_edge (int u, int v, int w) {
    e[tot].to = v;
    e[tot].nxt = head[u];
    e[tot].w = w;
    head[u] = tot++;
}
int rt , id;
int nowmn = INF;
int subtreesize;
int sz[nmax];
int dist[10], num[10];
ll ans = 0;
bool visit[nmax];
void getroot(int u, int f) {
    sz[u] = 1;
    int mxchild = 0;
    for(int i = head[u]; i != -1; i = e[i].nxt ) {
        int v = e[i].to;
        if(v != f && !visit[v]) {
            getroot(v, u);
            sz[u] += sz[v];
            mxchild = max(mxchild, sz[v]);
        }
    }
    int tmp = max(mxchild, subtreesize - sz[u]);
    if(tmp < nowmn) {
        nowmn = tmp;
        rt = u;
    }
}
void getdis(int u, int f, int dis) {
    dist[dis % 3] ++ ;
    for(int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if(v != f && !visit[v])
            getdis(v, u, (dis + e[i].w) % 3);
    }
}
ll getans(int u, int initdis) {
    dist[0] = dist[1] = dist[2] = 0;
    getdis(u, u, initdis);
//    for(int i = 0; i < id; ++i)
//        for(int j = 0; j < id; ++j)
//            if(tag == 1)
//                ++ num[(dist[i] + dist[j]) % 3];
//            else
//                -- num[(dist[i] + dist[j] + initdis) % 3];
    return 2ll * dist[1] * dist[2] + 1ll * dist[0] * dist[0];
}

void solve(int u) {
    ans += getans(u, 0);
    visit[u] = true;
    for (int i = head[u]; i != -1; i = e[i].nxt) {
        int v = e[i].to;
        if (!visit[v]) {
            ans -= getans(v, e[i].w);
            nowmn = INF; rt = 0; subtreesize = sz[v];
            getroot(v, u);
            solve(rt);
        }
    }
}
int main(){

    while( scanf("%d", &n) != EOF) {
        tot = 0;
        ans = 0;
        memset(head, -1, sizeof head);
        int u ,v, w;
        for(int i = 1; i <= n-1; ++i) {
            scanf("%d %d %d", &u, &v, &w);
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
        subtreesize = n; nowmn = INF;
        getroot(1, -1);
        solve(1);
        ll down = 1ll * n * n;
        ll up = ans;
        if(up == down) {
            printf("1/1\n");
        } else {
            ll gcd = __gcd(up, down);
            up /= gcd;
            down /= gcd;
            printf("%lld/%lld\n", up, down);
        }

//        printf("%d\n", num[m] * 2);
//        int q;
//        for(int i = 1; i <= m; ++i) {
//            scanf("%d", &q);
//            if(num[q])
//                printf("AYE\n");
//            else
//                printf("NAY\n");
//        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/pengwill97/article/details/82261237
今日推荐