点分治

点分治

树的重心

到所有点的距离之和最小

到其他点的最大距离最小

一棵树最多有两个重心,且相邻

两棵树通过一条边相连,新的重心在两棵树重心的链上

添加或删除一个节点,树的重心最多只移一条边的位置

求法

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) 出发的路径,对这些路径两两合并,就可以得到树上的简单路径。

但是这些路径中有些合并是不合法的。

贴一份那篇博客的代码

POJ-1741

#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
", 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
", p_sum / g, n * n / g);
}
原文地址:https://www.cnblogs.com/sduwh/p/13734115.html