【bzoj1036】[ZJOI2008]树的统计Count 树链剖分+线段树
题目描述
一棵树上有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本身
输入
输入的第一行为一个整数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之间。
输出
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
样例输入
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
样例输出
4
1
2
2
10
6
5
6
5
16
题解
树链剖分模板题,第一次码。。。
树链剖分就是把一棵树拆为一些链来处理,并用数据结构来维护。
通常的剖分方法是按照轻重链剖分,每次选取节点最多的子树继承重链,其余视作轻链,重新处理。
通常用线段树维护权值。
查询时,如果x和y在同一条链上,那么直接查询,否则一边将x和y往同一条链上靠,一边记录路径上的权值,然后查询。
代码应该不难理解。
#include <cstdio> #include <algorithm> using namespace std; #define inf 0x3fffffff #define lson l , mid , x << 1 #define rson mid + 1 , r , x << 1 | 1 int val[30005] , deep[30005] , si[30005] , fa[30005] , bl[30005] , pos[30005] , tot; int head[30005] , to[60005] , next[60005] , cnt; int maxn[120005] , sum[120005] , n; char str[10]; void add(int x , int y) { to[++cnt] =y; next[cnt] = head[x]; head[x] = cnt; } void dfs1(int x) { si[x] = 1; int i , y; for(i = head[x] ; i ; i = next[i]) { y = to[i]; if(y != fa[x]) { deep[y] = deep[x] + 1; fa[y] = x; dfs1(y); si[x] += si[y]; } } } void dfs2(int x , int c) { int k = 0 , i , y; pos[x] = ++tot; bl[x] = c; for(i = head[x] ; i ; i = next[i]) { y = to[i]; if(deep[y] > deep[x] && si[y] > si[k]) k = y; } if(k != 0) { dfs2(k , c); for(i = head[x] ; i ; i = next[i]) { y = to[i]; if(deep[y] > deep[x] && y != k) dfs2(y , y); } } } void pushup(int x) { sum[x] = sum[x << 1] + sum[x << 1 | 1]; maxn[x] = max(maxn[x << 1] , maxn[x << 1 | 1]); } void update(int p , int c , int l , int r , int x) { if(l == r) { sum[x] = maxn[x] = c; return; } int mid = (l + r) >> 1; if(p <= mid) update(p , c , lson); else update(p , c , rson); pushup(x); } int querysum(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return sum[x]; int mid = (l + r) >> 1 , ans = 0; if(b <= mid) ans += querysum(b , e , lson); if(e > mid) ans += querysum(b , e , rson); return ans; } int querymaxn(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return maxn[x]; int mid = (l + r) >> 1 , ans = -inf; if(b <= mid) ans = max(ans , querymaxn(b , e , lson)); if(e > mid) ans = max(ans , querymaxn(b , e , rson)); return ans; } int solvesum(int x , int y) { int ans = 0; while(bl[x] != bl[y]) { if(deep[bl[x]] < deep[bl[y]]) swap(x , y); ans += querysum(pos[bl[x]] , pos[x] , 1 , n , 1); x = fa[bl[x]]; } if(pos[x] > pos[y]) swap(x , y); ans += querysum(pos[x] , pos[y] , 1 , n , 1); return ans; } int solvemaxn(int x , int y) { int ans = -inf; while(bl[x] != bl[y]) { if(deep[bl[x]] < deep[bl[y]]) swap(x , y); ans = max(ans , querymaxn(pos[bl[x]] , pos[x] , 1 , n , 1)); x = fa[bl[x]]; } if(pos[x] > pos[y]) swap(x , y); ans = max(ans , querymaxn(pos[x] , pos[y] , 1 , n , 1)); return ans; } int main() { int i , x , y , q; scanf("%d" , &n); for(i = 1 ; i < n ; i ++ ) { scanf("%d%d" , &x , &y); add(x , y); add(y , x); } for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &val[i]); dfs1(1); dfs2(1 , 1); for(i = 1 ; i <= n ; i ++ ) update(pos[i] , val[i] , 1 , n , 1); scanf("%d" , &q); while(q -- ) { scanf("%s%d%d" , str , &x , &y); switch(str[1]) { case 'H': val[x] = y; update(pos[x] , y , 1 , n , 1); break; case 'S': printf("%d\n" , solvesum(x , y)); break; default: printf("%d\n" , solvemaxn(x , y)); } } return 0; }