Description
给出一棵n个点的树,和每条边的边权,求出有多少个点对,它们的距离为3的倍数。
最后输出求出的点对数/总点对数的最简分数。
Sample Input
5
1 2 1
1 3 2
1 4 1
2 5 3
Sample Output
13/25
点分治版题,来存一下板子。
#include <cstdio>
#include <cstring>
using namespace std;
int _max(int x, int y) {return x > y ? x : y;}
struct edge {
int x, y, d, next;
} e[81000]; int len, last[41000];
int ans, root, sum, f[41000], s[41000], tot[41000];
int tt[41000];
bool v[41000];
void ins(int x, int y, int d) {
e[++len].x = x; e[len].y = y; e[len].d = d;
e[len].next = last[x]; last[x] = len;
}
void getrt(int x, int fa) {
tot[x] = 1; f[x] = 0;
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
if(y != fa && !v[y]) {
getrt(y, x);
tot[x] += tot[y];
f[x] = _max(f[x], tot[y]);
}
}
f[x] = _max(f[x], sum - tot[x]);
if(f[x] < f[root]) root = x;
}
void getd(int x, int fa, int d) {
tt[d]++;
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
if(y != fa && !v[y]) {
getd(y, x, (d + e[k].d) % 3);
}
}
}
int cal(int x, int d) {
tt[0] = tt[1] = tt[2] = 0;
getd(x, 0, d % 3);
return tt[1] * tt[2] * 2 + tt[0] * tt[0];
}
int dfs(int x) {
v[x] = 1;
ans += cal(x, 0);
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
if(!v[y]) {
root = 0; f[root] = 999999999;
ans -= cal(y, e[k].d);
sum = tot[y];
getrt(y, x);
dfs(root);
}
}
}
int gcd(int a, int b) {
if(a == 0) return b;
return gcd(b % a, a);
}
int main() {
int n; scanf("%d", &n);
for(int i = 1; i < n; i++) {
int x, y, d; scanf("%d%d%d", &x, &y, &d);
ins(x, y, d); ins(y, x, d);
}
sum = n;
f[0] = 999999999; getrt(1, 0);
dfs(root);
int hh = gcd(ans, n * n);
printf("%d/%d\n", ans / hh, n * n / hh);
return 0;
}