1036: [ZJOI2008]树的统计Count
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 9697 Solved: 3931
[Submit][Status][Discuss]
Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身
Input
输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。
Output
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
Sample Input
4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
Sample Output
4
1
2
2
10
6
5
6
5
16
1
2
2
10
6
5
6
5
16
HINT
Source
ANALISIS
裸树剖。注意负数就行了。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 30005; int n; struct Edge { int from, to, next; Edge() { } Edge(int u, int v) : from(u), to(v) { } }edges[MAXN << 1]; int first[MAXN], tots; int fa[MAXN], top[MAXN], num[MAXN], dep[MAXN], bw[MAXN], sons[MAXN], cnt; int maxv[MAXN << 2], sumv[MAXN << 2], _max, _sum; void addedge(int u, int v) { edges[tots] = Edge(u, v); edges[tots].next = first[u]; first[u] = tots++; } void dfs1(int x, int d) { dep[x] = d; int maxson = 0; sons[x] = 1; for (int i = first[x]; i != -1; i = edges[i].next) if (dep[edges[i].to] == 0) { dfs1(edges[i].to, d + 1); sons[x] += sons[edges[i].to]; fa[edges[i].to] = x; if (sons[edges[i].to] > maxson) { maxson = sons[edges[i].to]; bw[x] = i; } } } void dfs2(int x, int t) { if (num[x]) return; num[x] = ++cnt; top[x] = t; if (bw[x] != -1) dfs2(edges[bw[x]].to, t); for (int i = first[x]; i != -1; i = edges[i].next) dfs2(edges[i].to, edges[i].to); } void update(int o, int L, int R, int v, int delx) { if (L == R) { maxv[o] = sumv[o] = delx; return; } int mid = (L + R) >> 1; int lc = o << 1, rc = lc + 1; if (mid >= v) update(lc, L, mid, v, delx); if (mid + 1 <= v) update(rc, mid + 1, R, v, delx); maxv[o] = max(maxv[lc], maxv[rc]); sumv[o] = sumv[lc] + sumv[rc]; } void query(int o, int L, int R, int y1, int y2) { if (y1 <= L && R <= y2) { _max = max(_max, maxv[o]); _sum += sumv[o]; return; } int mid = (L + R) >> 1; int lc = o << 1, rc = lc + 1; if (mid >= y1) query(lc, L, mid, y1, y2); if (mid + 1 <= y2) query(rc, mid + 1, R, y1, y2); } int main() { scanf("%d", &n); tots = 0; memset(first, -1, sizeof(first)); for (int i = 1; i < n; ++i) { int a, b; scanf("%d%d", &a, &b); addedge(a, b); addedge(b, a); } memset(dep, 0, sizeof(dep)); memset(sons, 0, sizeof(sons)); memset(bw, -1, sizeof(bw)); dfs1(1, 1); cnt = 0; memset(num, 0, sizeof(num)); dfs2(1, 1); memset(sumv, 0, sizeof(sumv)); for (int i = 1; i <= n; ++i) { int t; scanf("%d", &t); update(1, 1, n, num[i], t); } int q; scanf("%d", &q); while (q--) { char cmd[10]; int a, b; scanf("%s %d%d", cmd, &a, &b); if (cmd[0] == 'C') update(1, 1, n, num[a], b); else { _max = -60001; _sum = 0; for (;;) { int f1 = top[a], f2 = top[b]; if (f1 == f2) break; if (dep[f1] > dep[f2]) query(1, 1, n, num[f1], num[a]), a = fa[f1]; else query(1, 1, n, num[f2], num[b]), b = fa[f2]; } if (num[b] < num[a]) swap(a, b); query(1, 1, n, num[a], num[b]); if (cmd[1] == 'M') printf("%d ", _max); else printf("%d ", _sum); } } return 0; }