[树链剖分] P3313 [SDOI2014]旅行(动态开点 线段树)

P3313 [SDOI2014]旅行

https://www.luogu.org/problem/P3313
我们 单点修改城市的信仰 和 开销
询问 路径上 同信仰的城市 开销的花费和 or路径上最大值
显然 我们树链剖分完 直接建立 1e5 颗线段树 是最方便的 而且是单点修改 确保了我们时间上 和空间上的复杂度保证
我们考虑动态开点 来降低内存上的开销
注意细节。我在查询最大值 sum时 没有lc rc 我直接retrun 了 跳过了 后半区间的 值 orz。要注意细节orz 这bug 是我洗完澡回来重新看自己代码发现的

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, k, m;
int head[maxn], cnt;
int to[maxn << 1], nxt[maxn << 1];
int buff[maxn], ctma[maxn];
void ade(int a, int b) {
	to[++ cnt] = b;
	nxt[cnt] = head[a], head[a] = cnt;
}

int siz[maxn], son[maxn], fa[maxn], dep[maxn];
void dfs1(int u, int f) {
	fa[u] = f;
	dep[u] = dep[f] + 1;
	siz[u] = 1;
	int maxsize = -1;
	for(int i = head[u]; i; i = nxt[i]) {
		int v = to[i];
		if(v == f) continue;
		dfs1(v, u);
		siz[u] += siz[v];
		if(siz[v] > maxsize) {
			maxsize = siz[v];
			son[u] = v;
		}
	}
}

int tim, dfn[maxn], top[maxn], bel[maxn];
void dfs2(int u, int t) {
	dfn[u] = ++tim;
	top[u] = t;
	if(!son[u]) return ;
	dfs2(son[u], t);
	for(int i = head[u]; i; i = nxt[i]) {
		int v = to[i];
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}

struct node{
	int lc, rc, sum, ma;
}tr[maxn * 20];
int root[maxn], tot;

int build() {
	++ tot;
	tr[tot].lc = tr[tot].rc = tr[tot].sum = tr[tot].ma = 0;
	return tot;
}

void push_up(int rt) {
	tr[rt].sum = tr[tr[rt].lc].sum + tr[tr[rt].rc].sum;
	tr[rt].ma = max(tr[tr[rt].lc].ma, tr[tr[rt].rc].ma);
}

void insert(int p, int L, int l, int r, int val) {
	if(l == r) {
		tr[p].ma = tr[p].sum = val;
		return ;
	}
	int mid = l + r >> 1;
	if(L <= mid) {
		if(!tr[p].lc) tr[p].lc = build();
		insert(tr[p].lc, L , l, mid, val);
	} else {
		if(!tr[p].rc) tr[p].rc = build();
		insert(tr[p].rc, L, mid + 1, r, val);
	}
	push_up(p);
}

int querymax(int p, int L, int R, int l, int r) {
	if(L <= l && r <= R) return tr[p].ma;
	int mid = l + r >> 1;
	int res = 0;
	if(L <= mid) {
		if(!tr[p].lc) res = max(res, 0); 
		else res = max(res, querymax(tr[p].lc, L, R, l, mid));
	}
	if(R > mid) {
		if(!tr[p].rc) res = max(res, 0);
		else res = max(res, querymax(tr[p].rc, L, R, mid + 1, r));
	}
	return res;
}

int querysum(int p, int L, int R, int l, int r) {
	if(L <= l && r <= R) return tr[p].sum;
	int mid = l + r >> 1;
	int res = 0;
	if(L <= mid) {
		if(!tr[p].lc) res = res; 
		else res = (res + querysum(tr[p].lc, L, R, l, mid));
	}
	if(R > mid) {
		if(!tr[p].rc) res = res;
		else res = (res + querysum(tr[p].rc, L, R, mid + 1, r));
	}
	return res;
}

int qsumchain(int buff, int x, int y) {
	int ret = 0;
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		ret = (ret + querysum(root[buff], dfn[top[x]], dfn[x], 1, n));
		x = fa[top[x]];
	}
	if(dep[x] > dep[y]) swap(x, y);
	ret = (ret + querysum(root[buff], dfn[x], dfn[y], 1, n)); 
	return ret;
}

int qmaxchain(int buff, int x, int y) {
	int ret = 0;
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		ret = max(ret, querymax(root[buff], dfn[top[x]], dfn[x], 1, n));
		x = fa[top[x]];
	}
	if(dep[x] > dep[y]) swap(x, y);
	ret = max(ret, querymax(root[buff], dfn[x], dfn[y], 1, n)); 
	return ret;
}

signed main() {
	scanf("%d %d", &n, &m);
	tot = 0;
	for(int i = 1; i <= n; i ++) root[i] = build();
	for(int i = 1; i <= n; i ++) {
		scanf("%d %d", &ctma[i], &buff[i]);
	}
	for(int i = 1, a, b; i < n; i ++) {
		scanf("%d %d", &a, &b);
		 ade(a, b), ade(b, a);
	}
	dfs1(1, 1);
	dfs2(1, 1);
	for(int i = 1; i <= n; i ++) {
		if(!root[buff[i]]) root[buff[i]] = build();
		insert(root[buff[i]], dfn[i], 1, n, ctma[i]);
	}
	char op[15]; int x, y;
	while(m --) {
		scanf("%s %d %d", op, &x, &y);
		int buffs = buff[x];
		if(op[1] =='C') {
			insert(root[buffs], dfn[x], 1, n, 0);
			if(!root[y]) root[y] = build(); 
			buff[x] = y;
			insert(root[y], dfn[x], 1, n, ctma[x]);
		} else if(op[1] == 'W') {
			ctma[x] = y;
			insert(root[buffs], dfn[x], 1, n, ctma[x]);
		} else if(op[1] == 'S') {
			printf("%d\n", qsumchain(buffs, x, y));
		} else if(op[1] == 'M') {
			printf("%d\n", qmaxchain(buffs, x, y));
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值