题意
在一棵树上任取两点,问这两点间路径和为3倍数的最简概率
题解
树形Dp,f[x][0/1/2]记录x点以下(默认1为根) mod 3 = 0/1/2 的边的个数
\(\ f[x][0]\equiv0\)\(\pmod{3}\)
\(\ f[x][1]\equiv1\)\(\pmod{3}\)
\(\ f[x][2]\equiv2\)\(\pmod{3}\)
更新 ans += f[x][0] * f[cur][0] + f[x][1] * f[cur][2] + f[x][2] * f[cur][1]
f[x][0/1/2] += f[cur][0/1/2]
注意点
最后要求出真正ans,要约分
#include <cstdio>
#define maxn 20005
using namespace std;
int n, cnt, ans, head[maxn], f[maxn][3];
struct node{
int next, to, v;
}e[maxn << 1];
void AddEdge(int u, int v, int dis){
e[++cnt] = (node){head[u], v, dis}; head[u] = cnt;
e[++cnt] = (node){head[v], u, dis}; head[v] = cnt;
}
int gcd(int x, int y){
return (y == 0) ? x : gcd(y, x % y);
}
void dp(int x, int fa){
f[x][0] = 1;
for (int i = head[x]; i; i = e[i].next){
int cur = e[i].to;
if (cur != fa){
dp(e[i].to, x);
if (e[i].v % 3 == 1){
int tmp = f[cur][0];
f[cur][0] = f[cur][2];
f[cur][2] = f[cur][1];
f[cur][1] = tmp;
}
if (e[i].v % 3 == 2){
int tmp = f[cur][0];
f[cur][0] = f[cur][1];
f[cur][1] = f[cur][2];
f[cur][2] = tmp;
}
ans += f[x][0] * f[cur][0] + f[x][1] * f[cur][2] + f[x][2] * f[cur][1];
f[x][0] += f[cur][0], f[x][1] += f[cur][1], f[x][2] += f[cur][2];
}
}
}
int main(){
scanf("%d", &n);
for (int x, y, z, i = 1; i < n; i++){
scanf("%d%d%d", &x, &y, &z);
AddEdge(x, y, z);
}
dp(1, 0);
ans = ans * 2 + n;
int tmp = gcd(n * n, ans);
printf("%d/%d\n", ans / tmp, n * n / tmp);
return 0;
}