题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036
[ZJOI2008]树的统计Count
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 12941 Solved: 5206
Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. 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
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
1
2
2
10
6
5
6
5
16
HINT
Source
思路:树链剖分的模板题。此题是基于点权的操作,比较容易理解,不多解释。详见代码。
附上AC代码:
#include <bits/stdc++.h>
#define lrt rt<<1
#define rrt rt<<1|1
#define lson l, m, lrt
#define rson m+1, r, rrt
using namespace std;
const int maxn = 30005;
// 分别表示以当前节点作为根的子树的节点数目,
// 树上各个节点的初始值,当前节点的重儿子
int sizev[maxn], num[maxn], son[maxn];
// 分别表示树链上深度最小的节点,当前节点的深度,
// 原节点在剖分后的时间戳,即新的编号
int top[maxn], deep[maxn], pos[maxn];
// 分别表示当前时间戳对应的原节点编号,
// 当前节点的父节点
int level[maxn], p[maxn];
// 判断该节点是否被访问过了
bool vis[maxn];
// 分别表示节点数,询问数和时间戳计数
int n, q, cnt;
// 存储树的各个节点所连接的边
vector<int> edge[maxn];
void init(){
for (int i=1; i<=n; ++i){
sizev[i] = top[i] = son[i] = 0;
deep[i] = pos[i] = level[i] = 0;
p[i]=0, vis[i]=false;
cnt = 0;
edge[i].clear();
}
}
void add_edge(int u, int v){
edge[u].push_back(v);
edge[v].push_back(u);
}
void dfs1(int u, int root){
vis[u] = true;
sizev[u] = 1;
p[u] = root;
deep[u] = deep[root]+1;
int siz = edge[u].size();
for (int i=0; i<siz; ++i){
int v = edge[u][i];
if (v!=p[u] && !vis[v]){
dfs1(v, u);
sizev[u] += sizev[v];
if (son[u] == 0)
son[u] = v;
else if (sizev[son[u]] < sizev[v])
son[u] = v;
}
}
}
void dfs2(int u, int root){
vis[u] = true;
pos[u] = ++cnt;
level[cnt] = u;
top[u] = root;
if (son[u])
dfs2(son[u], root);
int siz = edge[u].size();
for (int i=0; i<siz; ++i){
int v = edge[u][i];
if (v!=p[u] && v!=son[u] && !vis[v])
dfs2(v, v);
}
}
int sumv[maxn<<2], maxv[maxn<<2];
int value[maxn];
char op[10];
void push_up(int rt){
sumv[rt] = sumv[lrt]+sumv[rrt];
maxv[rt] = max(maxv[lrt], maxv[rrt]);
}
void build(int l, int r, int rt){
if (l == r){
sumv[rt] = maxv[rt] = value[level[l]];
return ;
}
int m = (l+r)>>1;
build(lson);
build(rson);
push_up(rt);
}
void modify(int p, int val, int l, int r, int rt){
if (l == r){
sumv[rt] = val;
maxv[rt] = val;
return ;
}
int m = (l+r)>>1;
if (p <= m)
modify(p, val, lson);
else
modify(p, val, rson);
push_up(rt);
}
int query(int ql, int qr, int l, int r, int rt, bool ok){
if (ql<=l && r<=qr)
return ok ? sumv[rt] : maxv[rt];
int m = (l+r)>>1;
int ans = ok ? 0 : INT_MIN;
if (ql <= m){
int t = query(ql, qr, lson, ok);
ans = ok ? ans+t : max(ans, t);
}
if (qr > m){
int t = query(ql, qr, rson, ok);
ans = ok ? ans+t : max(ans, t);
}
return ans;
}
int seek(int x, int y, bool ok){
int ans = ok ? 0 : INT_MIN;
while (top[x] != top[y]){
if (deep[top[x]] < deep[top[y]])
swap(x, y);
int t = query(pos[top[x]], pos[x], 1, n, 1, ok);
ans = ok ? ans+t : max(ans, t);
x = p[top[x]];
}
if (deep[x] > deep[y])
swap(x, y);
int t = query(pos[x], pos[y], 1, n, 1, ok);
return ok ? ans+t : max(ans, t);
}
int main(){
while (~scanf("%d", &n)){
init();
int a, b;
for (int i=1; i<n; ++i){
scanf("%d%d", &a, &b);
add_edge(a, b);
}
for (int i=1; i<=n; ++i)
scanf("%d", value+i);
dfs1(1, 0);
memset(vis, false, sizeof(bool)*(n+1));
dfs2(1, 1);
build(1, cnt, 1);
scanf("%d", &q);
while (q--){
scanf("%s%d%d", op, &a, &b);
if (op[1] == 'H')
modify(pos[a], b, 1, cnt, 1);
else if (op[1] == 'M')
printf("%d\n", seek(a, b, false));
else
printf("%d\n", seek(a, b, true));
}
}
return 0;
}