[Vani有约会]雨天的尾巴 /【模板】线段树合并

我们在每一个节点上建立一个动态开点的线段树,最初只有根节点,线段树的叶节点下标维护的是物品类型,值为这种物品的个数。对于将x到y的路径上同时发放类型为w的物品,我们可以采用树上差分。这是对于点的差分,方法为:v[x] += 1, v[y] += 1, v[lca(x,y)] -= 1, v[ fa[lca(x, y)] -=1。对于普通的树上差分,就是再进行一遍dfs,统计前缀和。但是这道题的每一个节点都是一棵线段树,我们可以采用线段树合并来快速维护前缀和。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>

using namespace std;

const int N = 1e5 + 10;
pair<int, int> PII;

int anc[N][22], dep[N];
int n, m, root[N], head[N], cnt, idx, rg = 1e5, ans[N];

struct Node {
	int lc, rc;
	int v, pos;
} tr[N * 50];

struct Edge {
	int v, nxt;
} e[N << 1];

void AddEdge(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = head[u];
	head[u] = cnt;
}

void dfs(int u, int father) {
	anc[u][0] = father;
	dep[u] = dep[father] + 1;
	for(int i = 1; i <= 20; i ++) {
		anc[u][i] = anc[anc[u][i - 1]][i - 1];
	}
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if( v == father)	continue;
		dfs(v, u);
	}
}

int lca(int x, int y) {
	if( dep[x] < dep[y])	swap(x, y);
	for(int i = 20; i >= 0; -- i ) {
		if( dep[anc[x][i]] >= dep[y])
			x = anc[x][i];
	}
	if( x == y)    return x;
	for(int i = 20; i >= 0; -- i) {
		 if( anc[x][i] != anc[y][i])
		 	x = anc[x][i], y = anc[y][i];
	} 
	return anc[x][0];
}

void pushup(int u) {
	if( tr[tr[u].lc].v >= tr[tr[u].rc].v) {
		tr[u].v = tr[tr[u].lc].v;
		tr[u].pos = tr[tr[u].lc].pos;
	} else {
		tr[u].v = tr[tr[u].rc].v;
		tr[u].pos = tr[tr[u].rc].pos;
	}
}

void insert(int u, int l, int r, int k, int v) {
	if( l == r) {
		tr[u].v += v;
		tr[u].pos = l;
		return ;
	}
	int mid = l + r >> 1;
	if( k <= mid) {
		if( !tr[u].lc)    tr[u].lc = ++ idx;
		insert(tr[u].lc, l, mid, k, v);
	}
	else {
		if( !tr[u].rc)    tr[u].rc = ++ idx;
		insert(tr[u].rc, mid + 1, r, k, v);
	}
	pushup(u); 
}

int merge(int p, int q, int l, int r) {
	if( !p || !q) 
		return p + q;
	if( l == r) {
		tr[p].v += tr[q].v;
		return p;
	}
	int mid = l + r >> 1;
	tr[p].lc = merge(tr[p].lc, tr[q].lc, l, mid);
	tr[p].rc = merge(tr[p].rc, tr[q].rc, mid + 1, r);
	pushup(p);
	return p;
} 

void Sum(int u, int fa) {
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if( v == fa)	continue;
		Sum(v, u);
		root[u] = merge(root[u], root[v], 1, rg);
	} 
	if( tr[root[u]].v == 0)
		ans[u] = 0;
	else ans[u] = tr[root[u]].pos;
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++)	root[i] = i;
	idx = n;
	for(int i = 1; i < n;  i++) {
		int u, v;
		scanf("%d%d", &u, &v);
		AddEdge(u, v), AddEdge(v, u); 
	} 
	dfs(1, 0);
	while(m --) {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		insert(root[u], 1, rg, w, 1);
		insert(root[v], 1, rg, w, 1);
		int t = lca(u, v);
		insert(root[t], 1, rg, w, -1);
		insert(root[anc[t][0]], 1, rg, w, -1);
	}
	Sum(1, 0);
	for(int i = 1; i <= n; i ++)
	    printf("%d
", ans[i]);
	return 0;
} 

原文地址:https://www.cnblogs.com/wyy0804/p/13773320.html