2132: 美食群岛
Time Limit: 5 Sec Memory Limit: 512 Mb Submitted: 30 Solved: 14
Description
Wells来到了一个充满美食的群岛!
这个世界是由N个岛屿构成,由N-1座桥将这N个岛屿连接起来,每个岛屿上只生产一种美食,每个岛屿上美食都是不同的,第i种美食会给Wells带来一个幸福度 wi ,当然不同的美食给Wells带来的幸福度可能是相同的。
作为一个曾经的吃货,对于美食还是非常挑剔的,Wells秉持中庸之道,只会吃带来幸福度在[a,b]中的美食而且每种只会吃一次(毕竟最近Wells的压力真的非常大,吃不下太多……),当然不同时候Wells所认定的a,b是不同的。
现在Wells需要游历美食群岛,Wells想知道如果这时候从u出发到达v,在当时给定的[a,b]下,他最多可以获得多少幸福度。
当然,美食群岛也是一个动态的变动系统,也会在某个时刻更改生产美食的种类,更新幸福度。
那么问题来了,Wells很懒,需要聪明的你回答他的每次询问。
Input
多组数据,对于每组数据:
第一行给定两个整数 n,m(1≤n,m≤10^5), 表示岛屿个数和Wells的询问个数.
第二行 c1,c2,…,cn(1≤ci≤10^7),表示第i个岛屿的美食带给Wells的幸福度
接下来n-1行,每行两个整数x,y(1≤x,y≤n),表明第x个岛屿与第y个岛屿之间有一座桥相连。
接下来m行,每行开始有一个字符表明询问或者修改信息
若以Q开头,代表是询问信息,Q后将紧跟4个数字 s,t,a,b(1≤s,t≤n;1≤a≤b≤10^7)表明出发城市,目标城市,Wells选定的幸福度的区间。
若以C开头,代表修改操作,C后紧跟两个数字 x,y(x≤n,;1≤y≤10^7),表示将第x个岛屿的美食幸福度改为y。
Output
对于每个Q询问,输出一行,表示从s->t路径上满足[a,b]限制情况下Wells最大能获得的幸福度。
Sample Input
5 3
1 2 1 3 2
1 2
2 4
3 1
2 5
Q 4 5 1 3
Q 1 1 1 1
Q 3 5 2 3
Sample Output
7
1
4
传送门:戳这里
题解:树上链的问题首先想到用树剖,然后将两种操作用CDQ分治优化,下面讨论查询和修改两种操作:
①因为要求链上权值在[a,b]之间的点的权值之和,可以将查询分为[1,a-1]和[1,b],前者的贡献为负,后者的贡献为正;
②对于修改操作val[x]->y,可以看成先把权值减去val[x],再把权值加上y(注意能直接加上-val[x],因为cdq分治中需要按照val排序,val为负时这个操作就没有意义了,因为这个操作即会影响贡献为负的查询也会影响贡献为正的查询);因为树上一开始就有权值,因此可以看成n次权值0->val[u]的修改权值的操作。
此题还可以用树套树求解,解题报告:戳这里
#include<cstdio>
#include<string.h>
#include<iostream>
#include<assert.h>
#include<stack>
#include<queue>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long LL;
const int MX = 1e5 + 5;
const int inf = 0x3f3f3f3f;
struct Edge {
int v, nxt;
} E[MX * 2];
int head[MX], tot;
int top[MX], f[MX], dep[MX], sz[MX], son[MX], p[MX], fp[MX], totp;
void init() {
memset (head, -1, sizeof (head) );
tot = 0;
}
void add_edge (int u, int v) {
E[tot].v = v;
E[tot].nxt = head[u];
head[u] = tot++;
}
void dfs (int u, int fa, int deep) {
f[u] = fa; dep[u] = deep;
sz[u] = 1; son[u] = 0;
for (int i = head[u]; ~i; i = E[i].nxt) {
int v = E[i].v;
if (v == fa) continue;
dfs (v, u, deep + 1);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void rebuild (int u, int t) {
top[u] = t;
fp[totp] = u; p[u] = totp++;
if (son[u]) rebuild (son[u], t);
else return;
for (int i = head[u]; ~i; i = E[i].nxt) {
int v = E[i].v;
if (v == f[u] || v == son[u]) continue;
rebuild (v, v);
}
}
void pre_solve (int n) {
totp = 1;
dfs (1, 0, 1);
rebuild (1, 1);
}
struct Tree {
LL a[MX];
int n;
void init (int _n) {
n = _n;
for (int i = 0; i <= n; i++) a[i] = 0;
}
void add (int x, int v) {
for (int i = x; i <= n; i += i & -i) a[i] += v;
}
LL sum (int x) {
LL ret = 0;
for (int i = x; i > 0; i -= i & -i) ret += a[i];
return ret;
}
LL query (int l, int r) {
return sum (r) - sum (l - 1);
}
} T;
struct Que {
int op, u, v, t, x, id;
} q[MX * 3], tmp[MX * 3];
int val[MX], mark[MX];
LL ans[MX];
bool cmp (const Que& q1, const Que& q2) {
if (q1.id != q2.id) return q1.id < q2.id;
return q1.t < q2.t;
}
void cdq (int l, int r) {
if (l >= r) return;
int m = (l + r) >> 1, p1 = l, p2 = m + 1, i = 0;
cdq (l, m); cdq (m + 1, r);
int op, u, v, t, x, id;
int mark = 0;
if (l == 6 && r == 9) mark = 1;
while (p1 <= m || p2 <= r) {
if (p1 <= m && (p2 > r || q[p1].t <= q[p2].t) ) {
op = q[p1].op, u = q[p1].u, v = q[p1].v, t = q[p1].t, x = q[p1].x, id = q[p1].id;
tmp[i++] = q[p1++];
if (op == 2) T.add (p[x], v * t);
} else {
op = q[p2].op, u = q[p2].u, v = q[p2].v, t = q[p2].t, x = q[p2].x, id = q[p2].id;
tmp[i++] = q[p2++];
if (op == 1) {
LL &cnt = ans[id];
int f1 = top[u], f2 = top[v];
while (f1 != f2) {
if (dep[f1] < dep[f2]) {
swap (u, v);
swap (f1, f2);
}
cnt += x * T.query (p[f1], p[u]);
u = f[f1], f1 = top[u];
}
if (dep[u] > dep[v]) swap (u, v);
cnt += x * T.query (p[u], p[v]);
}
}
}
for (int j = l; j <= m; j++) if (q[j].op == 2) T.add (p[q[j].x], -q[j].v * q[j].t);
for (int j = 0; j < i; j++) q[l + j] = tmp[j];
}
int main() {
int n, m;
char op[2]; int u, v, a, b, x, y;
//freopen ("in.txt", "r", stdin);
while (~scanf ("%d%d", &n, &m) ) {
init();
for (int i = 1; i <= n; i++) scanf ("%d", &val[i]);
for (int i = 1; i < n; i++) {
scanf ("%d%d", &u, &v);
add_edge (u, v);
add_edge (v, u);
}
pre_solve (n);
int cnt = 0;
for (int i = 1; i <= n; i++) {
q[++cnt] = (Que) {2, 0, 1, val[i], i, 0};
}
for (int i = 1; i <= m; i++) {
scanf ("%s", op);
if (op[0] == 'Q') {
scanf ("%d%d%d%d", &u, &v, &a, &b);
q[++cnt] = (Que) {1, u, v, a - 1, -1, i};
q[++cnt] = (Que) {1, u, v, b, 1, i};
mark[i] = 1;
} else {
scanf ("%d%d", &x, &y);
q[++cnt] = (Que) {2, 0, 1, y, x, i};
q[++cnt] = (Que) {2, 0, -1, val[x], x, i};
//printf("[%d]\n",-val[x]);
val[x] = y;
mark[i] = 0;
}
}
sort (q + 1, q + cnt + 1, cmp);
T.init (n);
for (int i = 1; i <= m; i++) ans[i] = 0;
cdq (1, cnt);
for (int i = 1; i <= m; i++) {
if (mark[i]) printf ("%lld\n", ans[i]);
}
}
return 0;
}