BZOJ3307 雨天的尾巴 (动态开点 + 线段树合并 + 树上差分)

众所周知,线段树空间复杂度为O(N * 4),当 N 足够大时,容易爆掉。
动态开点,即字面意义,不必把整棵树的结构全部构建出来,只在最初建立一个结点表示整段区间,递归需要访问子树时再建立这个子树的结点。不再用完全二叉树编号两倍的规则。
动态开点线段树的优点:1.可以合并两个维护相同值域的线段树。2.节省空间,推动构建资源节约型社会
代码实现:

struct node {
	int l, r, s;//左右儿子编号和结点值
} e[N];
int build() {//新建一个结点,返回其编号
	tot++;
	e[tot].l = e[tot].r = e[tot].s = 0; 
	return tot;
}
void insert(int i, int l, int r, int pos, int k) {//单点修改
	if(l == r)  {
		e[i].s += k; e[i].num = pos;
		return ;
	}
	int mid = (l + r) >> 1;
	if(pos <= mid) {
		if(e[i].l == 0) e[i].l = build();//动态开点
		insert(e[i].l, l, mid, pos, k);
	} else {
		if(e[i].r == 0) e[i].r = build();//动态开点
		insert(e[i].r, mid + 1, r, pos, k);
	}
	update(i);
}

线段树合并:
有两种,一种是合并到新建结点,另一种是删除其中一个结点。
以后者为例:
从两个根结点出发,用两个变量标记当前的结点编号,同步递归遍历两棵树。
1.当其中一个为空时,返回另一个。
2.到达叶结点时将两个结点的值相加。

int merge(int p, int q, int l, int r) {
	if(!p) return q;
	if(!q) return p;//1
	if(l == r) {//2
		e[p].s += e[q].s; return p;
	}
	int mid = (l + r) >> 1;
	e[p].l = merge(e[p].l, e[q].l, l, mid);
	e[p].r = merge(e[p].r, e[q].r, mid + 1, r);
	update(p);
	return p;//只返回p,相当于删除q
}

BZOJ3307 雨天的尾巴 :
对于此题,我们对于树上每一个结点建一个线段树,线段树下标表示救济粮类型,维护其最大值。
采用树上差分的方法,读入\(x, y, z\)后,分别在\(x, y\)结点上 \(z\) 的对应位置 + 1 ,在\(LCA(x, y)\)结点对应位置 - 1 ,在\(Father(LCA(x, y))\)结点对应位置-1,这样保证了一个结点的一种救济粮数量等于其所有子结点的此类型救济粮数量之和。
修改完后,一遍DFS,将子树的线段树合并,查询最大值。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 100010;
const int M = 8000010;
int head[N], to[N << 1], nextt[N << 1], cnt = 0;
int n, m, a[N], b[N], c[N], t[N], maxn = 0;
int f[N][30], dep[N], tot = 0, root[N], ans[N];
void add(int x, int y) {
	nextt[++cnt] = head[x];
	to[cnt] = y; head[x] = cnt;
}
struct node {
	int l, r, s, num;//s表示数量,num表示数量最多的救济粮的类型
};
void dfs(int x, int fath) {//处理LCA倍增数组
	f[x][0] = fath;
	dep[x] = dep[fath] + 1;
	for(int i = 1; i <= 26; i++) f[x][i] = f[f[x][i - 1]][i - 1];
	for(int i = head[x]; i; i = nextt[i]) {
		if(to[i] != fath) dfs(to[i], x);
	}
}
int lca(int x, int y) {//LCA
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 26; i >= 0; i--) {
		if(dep[f[x][i]] >= dep[y]) x = f[x][i];
	}
	if(x == y) return x;
	for(int i = 26; i >= 0; i--) {
		if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	}
	return f[x][0];
}
struct Tree {
	node e[M];
	void update(int i) {
		if(e[e[i].l].s == 0) e[i].s = e[e[i].r].s, e[i].num = e[e[i].r].num;
		else if(e[e[i].r].s == 0) e[i].s = e[e[i].l].s, e[i].num = e[e[i].l].num;
		else if(e[e[i].l].s >= e[e[i].r].s) {
			e[i].s = e[e[i].l].s; e[i].num = e[e[i].l].num;
		} else {
			e[i].s = e[e[i].r].s; e[i].num = e[e[i].r].num;
		}
	}
	int build() {
		tot++;
		e[tot].l = e[tot].r = e[tot].s = e[tot].num = 0;
		return tot;
	}
	void insert(int i, int l, int r, int pos, int k) {
		if(l == r)  {
			e[i].s += k; e[i].num = pos;
			return ;
		}
		int mid = (l + r) >> 1;
		if(pos <= mid) {
			if(e[i].l == 0) e[i].l = build();
			insert(e[i].l, l, mid, pos, k);
		} else {
			if(e[i].r == 0) e[i].r = build();
			insert(e[i].r, mid + 1, r, pos, k);
		}
		update(i);
	}
	int get(int deg) {//查询deg结点中的最大数量的类型
		return e[root[deg]].s == 0 ? 0 : e[root[deg]].num;
	}
	int merge(int p, int q, int l, int r) {
		if(!p) return q;
		if(!q) return p;
		if(l == r) {
			e[p].s += e[q].s; return p;
		}
		int mid = (l + r) >> 1;
		e[p].l = merge(e[p].l, e[q].l, l, mid);
		e[p].r = merge(e[p].r, e[q].r, mid + 1, r);
		update(p);
		return p;
	}
} T;
void dfs1(int x, int fath) {
	for(int i = head[x]; i; i = nextt[i]) {
		if(to[i] == fath) continue;
		dfs1(to[i], x);
		root[x] = T.merge(root[x], root[to[i]], 1, maxn);
	}
	ans[x] = T.get(x);
}

int main() {
//	freopen("data.in", "r", stdin);
	scanf("%d%d", &n, &m);
	for(int i = 1, x, y; i < n; i++) {
		scanf("%d%d", &x, &y);
		add(x, y); add(y, x);
	}
	for(int i = 0; i <= n; i++) root[i] = T.build();//新建根结点,用root记录
	dfs(1, 0);
	for(int i = 1; i <= m; i++) {
		scanf("%d%d%d", &a[i], &b[i], &c[i]);
		maxn = max(maxn, c[i]);
	}
	maxn++;
	for(int i = 1; i <= m; i++) {
		T.insert(root[a[i]], 1, maxn, c[i], 1); T.insert(root[b[i]], 1, maxn, c[i], 1);
		int w = lca(a[i], b[i]);
		T.insert(root[w], 1, maxn, c[i], -1); T.insert(root[f[w][0]], 1, maxn, c[i], -1);
	}
	dfs1(1, 0);
	for(int i = 1; i <= n; i++) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2020-03-26 00:49  Mcggvc  阅读(194)  评论(0编辑  收藏  举报