1036: [ZJOI2008]树的统计Count
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3401 Solved: 1418
[ Submit][ Status]
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
一个树链剖分的入门题。 具体的树链剖分的 讲解推荐
http://www.cnblogs.com/937337156Zhang/p/6047165.html
http://www.cnblogs.com/sagitta/p/5660749.html
这俩配合使用。。。
然后我的代码是改的kuangbin的模板。。
//
// ZJOI树的统计count(树链剖分.cpp
// HDOJ
//
// Created by 张智超 on 01/10/2017.
// Copyright © 2017 张智超. All rights reserved.
//
#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <utility>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 30010;
struct Edge {
int v, next;
}edge[maxn << 1];
int head[maxn], total;//邻接表
int top[maxn]; //top[v] 表示v所在的重链的顶端节点
int fa[maxn]; //父亲节点
int deep[maxn];//深度
int son_num[maxn];//son_num[v]表示v为根的子树的节点数
int pos[maxn];//pos[v] v在线段树中的位置
int fpos[maxn];//和pos数组相反,由位置记录点
int son[maxn];//重儿子
int cur_pos;
inline void init() {
total = 0;
memset(head, -1, sizeof(head));
cur_pos = 0;
memset(son, -1, sizeof(son));
}
inline void add(int u, int v) {
edge[total] = {v, head[u]};
head[u] = total++;
}
//第一遍dfs求fa deep son_num son
void dfs1(int u, int pre, int de) {
deep[u] = de;
fa[u] = pre;
son_num[u] = 1;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if (v != pre) {
dfs1(v, u, de + 1);
son_num[u] += son_num[v];
if (son[u] == -1 || son_num[v] > son_num[son[u]])
son[u] = v;
}
}
}
void get_pos(int u, int sp) {
top[u] = sp;
pos[u] = ++cur_pos;
::fpos[pos[u]] = u;
if (son[u] == -1)
return;
get_pos(son[u], sp);
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].v;
if (v != son[u] && v != fa[u]) {
get_pos(v, v);
}
}
}
struct Node {
int left, right;
int sum;
int max;
}seg_tree[maxn << 2];
inline void push_up(int i) {
seg_tree[i].sum = seg_tree[i << 1].sum + seg_tree[i << 1 | 1].sum;
seg_tree[i].max = max(seg_tree[i << 1].max, seg_tree[i << 1 | 1].max);
}
int arr[maxn];
void build(int i, int left, int right) {
seg_tree[i].left = left;
seg_tree[i].right = right;
if (left == right) {
seg_tree[i].sum = seg_tree[i].max = arr[::fpos[left]];
return;
}
int mid = (left + right) >> 1;
build(i << 1, left, mid);
build(i << 1 | 1, mid + 1, right);
push_up(i);
}
//更新线段树第k个值为val
void update(int i, int k, int val) {
if (seg_tree[i].left == k && seg_tree[i].right == k) {
seg_tree[i].sum = seg_tree[i].max = val;
return;
}
int mid = (seg_tree[i].left + seg_tree[i].right) >> 1;
if (k <= mid)
update(i << 1, k, val);
else
update(i << 1 | 1, k, val);
push_up(i);
}
//查询线段树[left, right]区间最大值
int query_max(int i, int left, int right) {
if (seg_tree[i].left >= left && right >= seg_tree[i].right)
return seg_tree[i].max;
int mid = (seg_tree[i].left + seg_tree[i].right) >> 1;
int ans = -INF;
if (mid >= left)
ans = max(ans, query_max(i << 1, left, mid));
if (mid < right)
ans = max(ans, query_max(i << 1 | 1, left, right));
return ans;
}
int query_sum(int i, int left, int right) {
if (seg_tree[i].left >= left && right >= seg_tree[i].right) {
//cout <<" l " <<seg_tree[i].left << " begin " << left << " r " << seg_tree[i].right << " end" << right<< endl;
return seg_tree[i].sum;
}
int ans = 0;
int mid = (seg_tree[i].left + seg_tree[i].right) >> 1;
if (mid >= left)
ans += query_sum(i << 1, left, mid);
if (mid < right)
ans += query_sum(i << 1 | 1, left, right);
return ans;
}
//查询u->v路径上节点权值的和
int find_max(int u, int v) {
int f1 = top[u], f2 = top[v];
int tmp = -INF;
while (f1 != f2) {
if (deep[f1] < deep[f2]) {
swap(f1, f2);
swap(u, v);
}
tmp = max(tmp, query_max(1, pos[f1], pos[u]));
u = fa[f1];
f1 = top[u];
}
if (deep[u] > deep[v])
swap(u, v);
return max(tmp, query_max(1, pos[u], pos[v]));
}
//查询u->v路径上的节点的权值的和
int find_sum(int u, int v) {
int f1 = top[u], f2 = top[v];
int ans = 0;
while (f1 != f2) {
if (deep[f1] < deep[f2]) {
swap(u, v);
swap(f1, f2);
}
ans += query_sum(1, pos[f1], pos[u]);
//cout << "f1:"<<f1 << " u:" << u << " " << ans << endl;
u = fa[f1];
f1 = top[u];
}
if (deep[u] > deep[v])
swap(u, v);
return ans + query_sum(1, pos[u], pos[v]);
}
void print(int i, int l, int r) {
if (l == r) {
cout << seg_tree[i].sum << " ";
return;
}
int mid = (l + r) >> 1;
print(i << 1, l, mid);
print(i << 1 | 1, mid + 1, r);
}
int main() {
freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
int n, q, u, v;
char op[20];
while (cin >> n) {
init();
for (int i = 1; i < n; ++i) {
cin >> u >> v;
add(u, v);
add(v, u);
}
for (int i = 1; i <= n; ++i) {
cin >> arr[i];
}
dfs1(1, 0, 1);
get_pos(1, 1);
build(1, 1, cur_pos);
cin >> q;
while (q--) {
cin >> op >> u >> v;
//print(1, 1, cur_pos);
//cout << endl;
//cout << u << " " << v << endl;
if (op[0] == 'C') {
update(1, pos[u], v);
} else if (strcmp(op, "QMAX") == 0){
cout << find_max(u, v) << endl;
} else {
cout << find_sum(u, v) << endl;
}
}
}
}