树上启发式合并学习笔记

参考链接:https://www.cnblogs.com/zzqsblog/p/6146916.html

树上启发式合并可以在O(nlogn)的时间内解决很多关于树的无修改询问,比如每颗子树中出现的最多的颜色等。

树上启发式算法流程:

1 先dfs一次,记录每个节点的重儿子。(和树剖的预处理差不多)。

2 第二次dfs,这次dfs用来解决关于子树的询问的问题,分为3步:

  1 先dfs非重儿子节点,即解决非重儿子子树的询问问题,求解后把信息清零。

  2 dfs重儿子节点,即解决重儿子子树的询问问题,求解后不用把信息清零。

  3 用另一个dfs函数统计非重儿子的子树的信息(只是统计信息),之后便得到了这个节点的询问的答案。

  4 根据传进来的参数决定信息是否清零。

代码:

CF 600E:

#include<bits/stdc++.h>
using namespace std;
const int maxn =100010;
int head[maxn], Next[maxn*2], ver[maxn*2];
int cnt[maxn], col[maxn], sz[maxn], son[maxn], tot;
bool skip[maxn];
long long ans[maxn], sum;
int mx;
void add(int x, int y) {
	ver[++tot] = y;
	Next[tot] = head[x];
	head[x] = tot;
}
void get_son(int x,int f = 0) {
	sz[x] = 1;
	for(int i =head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == f) continue;
		get_son(y,x);
		sz[x] += sz[y];
		if(sz[y] > sz[son[x]])
			son[x] = y;
	}
}

void update(int x, int f, int k) {
	cnt[col[x]] += k;
	if(k > 0 && cnt[col[x]] >= mx) {
		if(cnt[col[x]] > mx)
			sum = 0, mx = cnt[col[x]];
		sum += col[x];
	}
	for(int i = head[x]; i; i = Next[i]) {
		int y  = ver[i];
		if(y != f && !skip[y])
			update(y,x,k);
	}
}

void dfs(int x, int f = 0, int kep = 0) {
	for(int i = head[x]; i ; i = Next[i]) {
		int y = ver[i];
		if(y != f && y != son[x]) {
			dfs(y, x);
		}
	}
	if(son[x]) {
		dfs(son[x], x, 1), skip[son[x]] = 1;
	}
	update(x,f,1);
	ans[x] = sum;
	if(son[x]) skip[son[x]] = 0;
	if(!kep) {
		update(x, f, -1);
		mx = sum = 0;
	}
}

int main() {
	int n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) scanf("%d", col + i);
	for(int i = 1; i < n; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y);
		add(y, x);
	}
	get_son(1);
	dfs(1);
	for(int i = 1; i <= n; i++)
		printf("%lld ", ans[i]);
}

CF 741D:

#include<bits/stdc++.h>
using namespace std;
const int maxn =500010;
int head[maxn], Next[maxn*2], ver[maxn*2], edge[maxn*2];
int cnt[maxn], sz[maxn], son[maxn], tot;
const int S = 'v' - 'a' + 1, INF = 1e9;
int lca_deep, skip, now;
int dp[maxn], re[1<<S], a[maxn], deep[maxn];
void add(int x, int y, int z) {
	ver[++tot] = y;
	edge[tot] = z;
	Next[tot] = head[x];
	head[x] = tot;
}

void get_son(int x,int f = 0) {
	sz[x] = 1;
	for (int i =head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == f) continue;
		a[y] = a[x] ^ edge[i];
		deep[y] = deep[x] + 1;
		get_son(y,x);
		sz[x] += sz[y];
		if(sz[y] > sz[son[x]])
			son[x] = y;
	}
}
void clr(int x) {
	re[a[x]] = -INF;
}
void change(int x) {
	now = max(now, re[a[x]] + deep[x]  -lca_deep * 2);
	for (int i = 0; i < S; i++) {
		now = max(now, re[a[x] ^ (1 << i)] + deep[x] - lca_deep * 2);
	}
}
void maintain(int x) {
	re[a[x]] = max(re[a[x]], deep[x]);
}
template <void(*func)(int)> 
void update(int x, int f) {
	func(x);
	for (int i = head[x]; i; i = Next[i]) {
		int y  = ver[i];
		if(y != f && y != skip)
			update<func>(y,x);
	}
}
void dfs(int x, int f = 0, int kep = 0) {
	for (int i = head[x]; i ; i = Next[i]) {
		int y = ver[i];
		if(y != f && y != son[x]) {
			dfs(y, x);
		}
	}
	if (son[x]) {
		dfs(son[x], x, 1), skip = son[x];
	}
	lca_deep = deep[x];
	for (int i  = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if (y != f) 
			dp[x] = max(dp[x], dp[y]);
	}
	for (int i = head[x]; i ; i = Next[i]) {
		int y = ver[i];
		if(y != f && y != son[x]) {
			update<change>(y,x);
			update<maintain>(y,x);
		}
	}
	change(x), maintain(x);
	dp[x] = max(dp[x], now);
	skip = 0;
	if (!kep) {
		update<clr>(x, f);
		now = -INF;
	}
}

int main() {
	int n;
	scanf("%d", &n);
	for(int i = 0; i < (1<<S); i++) {
		re[i] = - INF;
	} 
	for(int i = 2; i <= n; i++) {
		int x;
		char z[3];
		scanf("%d%s", &x, z);
		add(i, x, 1 << (z[0] - 'a'));
		add(x, i, 1 << (z[0] - 'a'));
	}
	get_son(1);
	dfs(1);
	for(int i = 1; i <= n; i++)
		printf("%d ", dp[i]);
}

  

猜你喜欢

转载自www.cnblogs.com/pkgunboat/p/10269687.html