点分治
点分治
树的重心
到所有点的距离之和最小
到其他点的最大距离最小
一棵树最多有两个重心,且相邻
两棵树通过一条边相连,新的重心在两棵树重心的链上
添加或删除一个节点,树的重心最多只移一条边的位置
求法
void getroot(int u, int f) { //f 是 father 节点
// 找质心
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) {
getroot(v, u);
sz[u] += sz[v];
mxchild = max(mxchild, sz[v]);
}
}
int tmp = max(mxchild, subtreesize - sz[u]); //父子树的最大节点个数
if(tmp < MX) {
MX = tmp;
rt = u;
}
}
点分治
感觉洛谷点分治模板 的题解写的不是很清楚
这篇写的不错
大概知道是怎么一回事了
点分治,一种针对可带权树上简单路径统计问题的算法。
一种带优化的暴力,加上一点容斥
每次找树的重心进行分治
把路径分为两种
- 经过 \(t\) 的路径
- 不经过 \(t\) 的路径
统计出从 \(t\) 出发的路径,对这些路径两两合并,就可以得到树上的简单路径。
但是这些路径中有些合并是不合法的。
贴一份那篇博客的代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#define F(i,a,b) for(int i=a;i<=(b);++i)
#define eF(i,u) for(int i=h[u];i;i=nxt[i])
using namespace std;
const int INF = 0x3f3f3f3f;
int n, k, Ans;
int h[10001], nxt[20001], to[20001], w[20001], tot;
inline void ins(int x, int y, int z) { nxt[++tot] = h[x]; to[tot] = y; w[tot] = z; h[x] = tot; }
bool vis[10001];
int Root, Tsiz, siz[10001], wt[10001];
int arr[10001], cnt;
void GetRoot(int u, int f) {
siz[u] = 1; wt[u] = 0;
eF(i, u) if (to[i] != f && !vis[to[i]])
GetRoot(to[i], u), siz[u] += siz[to[i]], wt[u] = max(wt[u], siz[to[i]]);
wt[u] = max(wt[u], Tsiz - siz[u]);
if (wt[Root] > wt[u]) Root = u;
}
void Dfs(int u, int D, int f) {
arr[++cnt] = D;
eF(i, u) if (to[i] != f && !vis[to[i]]) Dfs(to[i], D + w[i], u);
}
int calc(int u, int D) {
cnt = 0; Dfs(u, D, 0); int l = 1, r = cnt, sum = 0;
sort(arr + 1, arr + cnt + 1);
for (;; ++l) {
while (r && arr[l] + arr[r] > k) --r;
if (r < l) break;
sum += r - l + 1;
}
return sum;
}
void DFS(int u) {
Ans += calc(u, 0); vis[u] = 1;
eF(i, u) if (!vis[to[i]]) {
Ans -= calc(to[i], w[i]);
Root = 0, Tsiz = siz[to[i]], GetRoot(to[i], 0);
DFS(Root);
}
}
int main() {
int x, y, z;
while (~scanf("%d%d", &n, &k) && n && k) {
tot = Ans = 0; memset(vis, 0, sizeof vis); memset(h, 0, sizeof h);
F(i, 2, n) scanf("%d%d%d", &x, &y, &z), ins(x, y, z), ins(y, x, z);
wt[0] = INF; Tsiz = n; GetRoot(1, 0);
DFS(Root);
printf("%d\n", Ans - n);
}
return 0;
}
聪聪可可
输出树上长度是3的倍数的数目与所有路径之比
不是\(C_n^2\) 是 \(n^2\)
#include<bits/stdc++.h>
using namespace std;
#define repE(i,u) for(int i = head[u];i;i=nxt[i])
#define rep(i,a,b) for(int i = a;i <= b;i++)
const int maxn = 3e5 + 10;
int head[maxn], w[maxn], to[maxn], nxt[maxn], tot;
int n, k, m;
int MX;
bool vis[maxn];
int Tsz, sz[maxn], mx[maxn], Root;
int cnt, dis[maxn];
map<int, int>ans;
int p_sum;
void addEdge(int x, int y, int z) {
nxt[++tot] = head[x]; to[tot] = y; w[tot] = z; head[x] = tot;
}
void Getroot(int u, int f) {
sz[u] = 1, mx[u] = 0;
repE(i, u) {
if (to[i] != f and !vis[to[i]]) {
Getroot(to[i], u);
sz[u] += sz[to[i]];
mx[u] = max(mx[u], sz[to[i]]);
}
mx[u] = max(mx[u], Tsz - sz[u]);
if (mx[Root] > mx[u]) {
Root = u;
}
}
}
void dfs(int u, int len, int f) {
dis[cnt++] = len;
repE(i, u) {
if (to[i] != f and !vis[to[i]]) {
dfs(to[i], len + w[i], u);
}
}
}
void cal(int u, int w, int val) {
cnt = 0; dfs(u, w, 0);
int p[3] = { 0 };
for (int i = 0; i < cnt; i++) {
p[dis[i] % 3]++;
}
p_sum += val * (p[0] * p[0] + 2 * p[1] * p[2]);
}
void DFS(int u) {
cal(u, 0, 1); vis[u] = 1;
repE(i, u) {
if (!vis[to[i]]) {
cal(to[i], w[i], -1);
Root = 0, Tsz = sz[to[i]], Getroot(to[i], 0);
DFS(Root);
}
}
}
int query[maxn];
int gcd(int a, int b) { return a % b == 0 ? b : gcd(b, a % b); }
int main() {
scanf("%d", &n);
int x, y, z;
rep(i, 1, n - 1) scanf("%d%d%d", &x, &y, &z), addEdge(x, y, z), addEdge(y, x, z);
Tsz = n; mx[0] = 0x3f3f3f3f;
Getroot(1, 0);
DFS(Root);
int g = gcd(p_sum, n * n);
printf("%d/%d\n", p_sum / g, n * n / g);
}