题意
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。
他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画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;
}