P1505 [国家集训队]旅游

P1505 [国家集训队]旅游

题目背景

​ Ray 乐忠于旅游,这次他来到了 T 城。T 城是一个水上城市,一共有 n个景点,有些景点之间会用一座桥连接。为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有一条路径。换句话说, T 城中只有 n−1 座桥。

​ Ray 发现,有些桥上可以看到美丽的景色,让人心情愉悦,但有些桥狭窄泥泞,令人烦躁。于是,他给每座桥定义一个愉悦度 w,也就是说,Ray 经过这座桥会增加 w 的愉悦度,这或许是正的也可能是负的。有时,Ray 看待同一座桥的心情也会发生改变。

​ 现在,Ray 想让你帮他计算从 u 景点到 v 景点能获得的总愉悦度。有时,他还想知道某段路上最美丽的桥所提供的最大愉悦度,或是某段路上最糟糕的一座桥提供的最低愉悦度。

题目描述

给定一棵 n 个节点的树,边带权,编号 $ 0∼n−1$,需要支持五种操作:

  • C i w 将输入的第 i 条边权值改为 w
  • N u v 将 u,v 节点之间的边权都变为相反数
  • SUM u v 询问 u,v节点之间边权和
  • MAX u v 询问 u,v 节点之间边权最大值
  • MIN u v 询问 u,v 节点之间边权最小值

保证任意时刻所有边的权值都在 [−1000,1000] 内。

很经典的树剖题,简直就是模板,思路很好想,但是细节超多,我交了十几次才A。

细节1:取反的时候,原最大值变为现最小值,原最小值变为现最大值,在交换的时候要记录原值,直接交换会出错;

int tmp = t[o].Max; t[o].Max = -t[o].Min; t[o].Min = -tmp;
//错误写法:t[o].Max = -t[o].Min; t[o].Min = -t[o].Max;

细节2:题目告诉你权值有负数,在找最大值时,初值应该赋一个极小值,不能赋0;

int querymax(int o, int l, int r, int x, int y) {
    if(x <= l && y >= r) { return t[o].Max; }
    down(o);
    int res = -1e9; //*********
    if(x <= mid) res = max(res, querymax(ls(o), l, mid, x, y));
    if(y > mid) res = max(res, querymax(rs(o), mid + 1, r, x, y));
    return res;
}

细节3:题目给的是边权,我们把边权赋到子节点去做,我们发现根节点是没有值的,自然查询最值时不能算上;

int querypathmax(int x, int y) {
	int res = -1e9;
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		int fx = top[x]; res = max(res, cj.querymax(dfn[fx], dfn[x]));
		x = fa[fx];
	}
	if(dep[y] > dep[x]) swap(x, y);
	res = max(res, cj.querymax(dfn[y] + 1, dfn[x])); //这里+1就是为了不算根节点
	return res;
}
//错误写法:res = max(res, cj.querymax(dfn[y], dfn[x]));

细节4:很多人可能最后一个点过不去,我看题解也有几篇被最后一个点hack了,它的数据是这样的:

3
2 0 9  //第一条边,对应的点是2
1 0 8  //第二条边,对应的点是1
8
SUM 1 0
C 2 7
SUM 1 2
SUM 1 0
MAX 1 2
C 1 8
MAX 1 2
MIN 1 0

我们在执行操作"C"时,如果直接用边的编号当做点的编号就会错,所以我们应该开一个数组去找一条边对应的点:

void get_tree(int x, int Fa){
	dep[x] = dep[fa[x] = Fa] + 1; siz[x] = 1;
	for(int i = head[x]; i; i = e[i].nxt) {
		int y = e[i].to; if(y == Fa) continue;
		a[y] = e[i].val; fin[e[i].id] = y; //e[i].id是这条边的编号(正向边反向边编号相同),它对应的点是y;
		get_tree(y, x); siz[x] += siz[y];
		if(siz[y] > siz[hav[x]]) hav[x] = y;
	}
}
if(opt == "C") {
	cin >> x >> y; cj.changenode(dfn[fin[x]], y); 
}

AC代码:

#include <iostream>
#include <cstdio>
#include <cctype>
#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)

using namespace std;

const int N = 2e5 + 5;
int n, m, cnt, tot;
int a[N], A[N], fa[N], top[N], dep[N], siz[N], dfn[N], fin[N], hav[N], head[N];
struct edge { int nxt, to, id, val; } e[N << 1];

void add(int x, int y, int z, int i) {
	e[++cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].val = z; e[cnt].id = i;
}

void get_tree(int x, int Fa){
	dep[x] = dep[fa[x] = Fa] + 1; siz[x] = 1;
	for(int i = head[x]; i; i = e[i].nxt) {
		int y = e[i].to; if(y == Fa) continue;
		a[y] = e[i].val; fin[e[i].id] = y;
		get_tree(y, x); siz[x] += siz[y];
		if(siz[y] > siz[hav[x]]) hav[x] = y;
	}
}

void get_top(int x, int topic) {
	A[dfn[x] = ++tot] = a[x]; top[x] = topic;
	if(!hav[x]) return ; get_top(hav[x], topic); //********
	for(int i = head[x]; i; i = e[i].nxt) {
		int y = e[i].to; if(y == fa[x] || y == hav[x]) continue;
		get_top(y, y);
	}
}

struct Tree {
	protected:
		struct tree { long long sum; int Min, Max, tag; } t[N << 2];
		void up(int o) {
			t[o].sum = t[ls(o)].sum + t[rs(o)].sum;
			t[o].Max = max(t[ls(o)].Max, t[rs(o)].Max);
			t[o].Min = min(t[ls(o)].Min, t[rs(o)].Min);
		}
		void modify(int o, int k) {
			t[o].tag *= k; t[o].sum *= k; int tmp = t[o].Max; t[o].Max = -t[o].Min; t[o].Min = -tmp; //***tmp****
		}
		void down(int o) {
			if(t[o].tag == -1) modify(ls(o), -1), modify(rs(o), -1); t[o].tag = 1;
		}
		void build(int o, int l, int r) {
			t[o].tag = 1;
			if(l == r) { t[o].sum = t[o].Min = t[o].Max = A[l]; return ; }
			build(ls(o), l, mid); build(rs(o), mid + 1, r);
			up(o); 	
		}
		void changerode(int o, int l, int r, int x, int y) {
			if(x <= l && y >= r) { modify(o, -1); return ;}
			down(o);
			if(x <= mid) changerode(ls(o), l, mid, x, y);
			if(y > mid) changerode(rs(o), mid + 1, r, x, y);
			up(o);
		}
		void changenode(int o, int l, int r, int x, int k) {
			if(l == r) { t[o].sum = t[o].Max = t[o].Min = k; return ; }
			down(o);
			if(x <= mid) changenode(ls(o), l, mid, x, k);
			if(x > mid) changenode(rs(o), mid + 1, r, x, k);
			up(o);
		}
		long long querysum(int o, int l, int r, int x, int y) {
			if(x <= l && y >= r) { return t[o].sum; }
			down(o);
			long long res = 0;
			if(x <= mid) res += querysum(ls(o), l, mid, x, y);
			if(y > mid) res += querysum(rs(o), mid + 1, r, x, y);
			return res;
		}
		int querymax(int o, int l, int r, int x, int y) {
			if(x <= l && y >= r) { return t[o].Max; }
			down(o);
			int res = -1e9; //*********
			if(x <= mid) res = max(res, querymax(ls(o), l, mid, x, y));
			if(y > mid) res = max(res, querymax(rs(o), mid + 1, r, x, y));
			return res;
		}
		int querymin(int o, int l, int r, int x, int y) {
			if(x <= l && y >= r) { return t[o].Min; }
			down(o);
			int res = 1e9;
			if(x <= mid) res = min(res, querymin(ls(o), l, mid, x, y));
			if(y > mid) res = min(res, querymin(rs(o), mid + 1, r, x, y));
			return res;
		}
	public:
		void build() { build(1, 1, n); }
		void changerode(int x, int y) { changerode(1, 1, n, x, y); }
		void changenode(int x, int y) { changenode(1, 1, n, x, y); }
		long long querysum(int x, int y) { return querysum(1, 1, n, x, y); }
		int querymax(int x, int y) { return querymax(1, 1, n, x, y); }
		int querymin(int x, int y) { return querymin(1, 1, n, x, y); }
} cj;

void changepath(int x, int y) {
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		int fx = top[x]; cj.changerode(dfn[fx], dfn[x]);
		x = fa[fx];
	}
	if(dep[y] > dep[x]) swap(x, y);
	cj.changerode(dfn[y] + 1, dfn[x]);
}

long long querypathsum(int x, int y) {
	long long res = 0;
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		int fx = top[x]; res += cj.querysum(dfn[fx], dfn[x]);
		x = fa[fx];
	}
	if(dep[y] > dep[x]) swap(x, y);
	res += cj.querysum(dfn[y] + 1, dfn[x]);
	return res;
}

int querypathmax(int x, int y) {
	int res = -1e9;
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		int fx = top[x]; res = max(res, cj.querymax(dfn[fx], dfn[x]));
		x = fa[fx];
	}
	if(dep[y] > dep[x]) swap(x, y);
	res = max(res, cj.querymax(dfn[y] + 1, dfn[x])); 
	return res;
}

int querypathmin(int x, int y) {
	int res = 1e9;
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		int fx = top[x]; res = min(res, cj.querymin(dfn[fx], dfn[x]));
		x = fa[fx];
	}
	if(dep[y] > dep[x]) swap(x, y);
	res = min(res, cj.querymin(dfn[y] + 1, dfn[x]));
	return res;
}

int main() {

	cin >> n;
	for(int i = 1, x, y, z;i <= n - 1; i++) {
		cin >> x >> y >> z; x += 1; y += 1;
		add(x, y, z, i); add(y, x, z, i);
	}
	get_tree(1, 0); get_top(1, 1); cj.build();
	cin >> m;
	for(int i = 1;i <= m; i++) {
		string opt; int x, y; 
		cin >> opt;
		if(opt == "C") {
			cin >> x >> y; cj.changenode(dfn[fin[x]], y); 
		}
		else if(opt == "N") {
			cin >> x >> y; x += 1; y += 1; changepath(x, y);
		}
		else if(opt == "SUM") {
			cin >> x >> y; x += 1; y += 1; printf("%lld\n", querypathsum(x, y));
		}
		else if(opt == "MAX") {
			cin >> x >> y; x += 1; y += 1; printf("%d\n", querypathmax(x, y));
		}
		else {
			cin >> x >> y; x += 1; y += 1; printf("%d\n", querypathmin(x, y));
		}
	}

	return 0;
}
posted @ 2020-07-30 19:21  C锥  阅读(134)  评论(4编辑  收藏  举报