题意
思考
最近学习一下点分治
本题点分治裸题,也可以用树形 (dp) 做,在此记录一下点分治的做法:
首先题目要求求出边权和为 (3) 的倍数的路径个数和,那么我们可以将路径和对 (3) 取模,树上路径就只分为了三种: (0, 1, 2),用一个桶记录个数,那么每次点分治计算的答案就是 (sum[0] * sum[0] + sum[1] * sum[2]),(两条路径为(0)的链和一条路径为(1)的链(+)一条路径为(2)的链,由于后者的起点和中点的位置可以互换,所以就是乘法原理~)
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 20020;
struct node{
int nxt, to, dis;
}edge[N << 1];
int head[N], num;
void build(int from, int to, int dis){
edge[++num].nxt = head[from];
edge[num].to = to;
edge[num].dis = dis;
head[from] = num;
}
int dist[N], sz[N], vis[N], f[N], sum, root, ans[5], ANS;
void getroot(int u, int fa){
sz[u] = 1; f[u] = 0;
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa || vis[v]) continue;
getroot(v, u);
sz[u] += sz[v];
f[u] = max(f[u], sz[v]);
}
f[u] = max(f[u], sum - sz[u]);
if(f[u] < f[root]) root = u;
}
void getdep(int u, int fa){
dist[u] %= 3;
ans[dist[u]] ++;
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa || vis[v]) continue;
dist[v] = dist[u] + edge[i].dis;
getdep(v, u);
}
}
int calc(int u, int nowd){
dist[u] = nowd;
ans[0] = ans[1] = ans[2] = 0;
getdep(u, 0);
return ans[0] * ans[0] + ans[1] * ans[2] * 2;
}
void solve(int u, int fa){
ANS += calc(u, 0); vis[u] = 1;
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa || vis[v]) continue;
ANS -= calc(v, edge[i].dis);
root = 0;
sum = sz[v];
getroot(v, u);
solve(root, 0);
}
}
int n;
int gcd(int x, int y){
if(y == 0) return x;
return gcd(y, x % y);
}
int main(){
cin >> n;
for(int i=1; i<=n-1; i++){
int u, v, d;
cin >> u >> v >> d;
build(u, v, d); build(v, u, d);
}
sum = n; f[0] = 0x3f3f3f3f; root = 0;
getroot(1, 0);
solve(root, 0);
int GCD = gcd(ANS, n * n);
cout << ANS / GCD << "/" << n * n / GCD;
return 0;
}
总结
点分治多注意一下细节就好了,记得考虑两个点在一个子树内的情况要减掉