【POJ 3237】树的维护

相信你们都有题目了。。。(我就偷个懒)
想说,为什么一道模板题做了这么久?(好累)
首先,我的想法是将点换为边的编号来存储,码了半天,没码出来,把自己脑子搞昏了,就将边换为点存储。具体方法如下:

首先,我们发现一个点与它的爸爸的边和它本身是一一对应关系,受到启发我们用点来保存边权。

其次,因为有取相反数的操作,我终于灵机一动:存储max和min,取反时再倒过来。

大体思路有了,我们还有几个实际问题要解决。

  1. CHANGE操作是用边来处理,我们可以再用一个op数组存储对应关系。
  2. 虽然上文有匹配了,可以发现1号节点是没有匹配的边的。但如果放任不管的话,如果最终答案是负数,从这个节点上来最终答案会变成0。所以我们要赋初值。
  3. 然后,我们需要改进一下查询。当x和y不在一条链(这里指重链或轻链)时可以这么写,因为正好算出连接2条链的边。而如果在一条链上就会错:查询x和f[x]之间的边(只有一条),这样算会算爸爸的爸爸的边。
#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
const int N = 10002, INF = (1 << 30) - 1;
struct node {
	int u, num;
};
struct tree {
	int mx, mn;
}st[N << 2];
string ac;
int la[N << 2], op[N], son[N], ans, cnt, n, w[N], d[N], f[N], s[N], top[N], id[N];
vector <node> G[N];
void init(const int x, const int ba) {
	s[x] = 1;
	d[x] = d[ba] + 1;
	f[x] = ba;
	for(int i = 0; i < G[x].size(); i ++) {
		int v = G[x][i].u;
		if(v == ba)
			continue;
		op[G[x][i].num] = v;
		init(v, x);
		s[x] += s[v];
		if(s[v] > s[son[x]] || son[x] == 0)
			son[x] = v;
	}
}
void dfs(const int x, const int t) {
	id[x] = ++ cnt;
	top[x] = t;
	if(son[x] == 0)
		return;
	dfs(son[x], t);
	for(int i = 0; i < G[x].size(); i ++) {
		int v = G[x][i].u;
		if(v == f[x] || v == son[x])
			continue;
		dfs(v, v);
	}
}
void pushDown(const int x) {
	int l = (x << 1), r = (x << 1 | 1);
	if(la[x] == -1) {
		swap(st[l].mx, st[l].mn);
		swap(st[r].mx, st[r].mn);
		st[l].mx *= -1;
		st[l].mn *= -1;
		st[r].mx *= -1;
		st[r].mn *= -1;
	}
	la[l] *= la[x];
	la[r] *= la[x];
	la[x] = 1;
}
void addC(const int x, const int l, const int r, const int L, const int R, const int k) {
	if(l > R || r < L)
		return;
	if(l >= L && r <= R) {
		if ( L == 1 && R == 1 ) {
			st[x].mn = k;
			st[x].mx = -k;
		}
		else {
			st[x].mn = k;
			st[x].mx = k;
		}
		return;
	}
	pushDown(x);
	int mid = l + r >> 1;
	addC(x << 1, l, mid, L, R, k);
	addC(x << 1 | 1, mid + 1, r, L, R, k);
	st[x].mx = max(st[x << 1].mx, st[x << 1 | 1].mx);
	st[x].mn = min(st[x << 1].mn, st[x << 1 | 1].mn);
}
void addN(const int x, const int l, const int r, const int L, const int R) {
	if(l > r)
		return;
	if(l > R || r < L)
		return;
	if(l >= L && r <= R) {
		swap(st[x].mn, st[x].mx);
		st[x].mn *= -1;
		st[x].mx *= -1;
		la[x] = -la[x];//D
		return;
	}
	pushDown(x);
	int mid = l + r >> 1;
	addN(x << 1, l, mid, L, R);
	addN(x << 1 | 1, mid + 1, r, L, R);
	st[x].mx = max(st[x << 1].mx, st[x << 1 | 1].mx);
	st[x].mn = min(st[x << 1].mn, st[x << 1 | 1].mn);
}
int ask(const int x, const int l, const int r, const int L, const int R) {
	if(l > r)
		return -INF;//C
	if(l > R || r < L)
		return -INF;
	if(l >= L && r <= R)
		return st[x].mx;
	pushDown(x);
	int mid = l + r >> 1;
	return max(ask(x << 1, l, mid, L, R), ask(x << 1 | 1, mid + 1, r, L, R));
}
int main() {
	int a, b, x, y;
	scanf("%d", &n);
	for(int i = 1; i < n; i ++) {
		scanf("%d %d %d", &a, &b, &w[i]);
		G[a].push_back((node) {b, i});
		G[b].push_back((node) {a, i});
	}
	init(1, 0);
	dfs(1, 1);
	addC(1, 1, cnt, id[1], id[1], INF);
	for(int i = 1; i < n; i ++)
		addC(1, 1, cnt, id[op[i]], id[op[i]], w[i]);//A
	while(1) {
		cin >> ac;
		if(ac[0] == 'D')
			break;
		else if(ac[0] == 'Q') {
			scanf("%d %d", &x, &y);
			ans = -INF;//B
			while(top[x] != top[y]) {
				if(d[top[x]] >= d[top[y]]) {
					ans = max(ans, ask(1, 1, cnt, id[top[x]], id[x]));
					x = f[top[x]];
				}
				else {
					ans = max(ans, ask(1, 1, cnt, id[top[y]], id[y]));
					y = f[top[y]];
				}
			}
			if(id[x] <= id[y])
				ans = max(ans, ask(1, 1, cnt, id[x] + 1, id[y]));
			else
				ans = max(ans, ask(1, 1, cnt, id[y] + 1, id[x]));
			printf("%d\n", ans);
		}
		else if(ac[0] == 'C') {
			scanf("%d %d", &a, &b);
			addC(1, 1, cnt, id[op[a]], id[op[a]], b);
		}
		else {
			scanf("%d %d", &x, &y);
			while(top[x] != top[y]) {
				if(d[top[x]] >= d[top[y]]) {
					addN(1, 1, cnt, id[top[x]], id[x]);
					x = f[top[x]];
				}
				else {
					addN(1, 1, cnt, id[top[y]], id[y]);
					y = f[top[y]];
				}
			}
			if(id[x] <= id[y])
				addN(1, 1, cnt, id[x] + 1, id[y]);
			else
				addN(1, 1, cnt, id[y] + 1, id[x]);
		}
	}
	return 0;
}

注:
A:这个地方要用id数组哟,我栽在这好久。
B:ans不要赋值为0!!!!!!
C:l可能大于r,返回极小值。
D:此时不能直接赋值为-1,因为会有重复取反的操作。

好了就到这里了,如有错误,请在评论区指出,谢谢!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值