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 条边权值改为 wN 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;
}