题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966
题意:给一棵树,并给定各个点权的值,然后有3种操作:
I C1 C2 K: 把C1与C2的路径上的所有点权值加上K
D C1 C2 K:把C1与C2的路径上的所有点权值减去K
Q C:查询节点编号为C的权值
题解:就是树链剖分具体看代码,还有注释。
#include <iostream> #include <cstring> using namespace std; const int M = 5e4 + 10; struct Edge { int v , next , w; }edge[M << 1]; int head[M] , e;//链式前向星存边 int top[M];//重链的根节点 int fa[M];//fa[i]表示i的父节点 int deep[M];//节点的深度 int num[M];//num[i]表示i节点子树一共有多少节点 int p[M];//p[i]表示i点在线段树中的位置 int fp[M];//fp[i]表示线段树中位置为i的节点是什么 int son[M];//sum[u]表示u点的重儿子 int pos; void init() { memset(head , -1 , sizeof(head)); memset(son , -1 , sizeof(son)); e = 0; pos = 1; }//初始化 void add(int u , int v) { edge[e].v = v; edge[e].next = head[u]; head[u] = e++; }//加边 void dfs1(int u , int pre , int d) { deep[u] = d; fa[u] = pre; 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 , d + 1); num[u] += num[v]; if(son[u] == -1 || num[v] > num[son[u]]) { son[u] = v; } } } }//寻找重点 void getpos(int u , int sp) { top[u] = sp; p[u] = pos++; fp[pos - 1] = u; if(son[u] == -1) return ; getpos(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]) { getpos(v , v); } } }//构建重链 struct TnT { int l , r , sum , lazy; }T[M << 2]; int a[M]; void build(int l , int r , int i) { int mid = (l + r) >> 1; T[i].l = l , T[i].r = r , T[i].lazy = 0 , T[i].sum = 0; if(T[i].l == T[i].r) { T[i].sum = a[fp[l]]; return ; } build(l , mid , i << 1); build(mid + 1 , r , (i << 1) | 1); T[i].sum = T[i << 1].sum + T[(i << 1) | 1].sum; } void pushdown(int i) { if(T[i].lazy) { T[i << 1].sum += T[i].lazy * (T[i << 1].r - T[i << 1].l + 1); T[(i << 1) | 1].sum += T[i].lazy * (T[(i << 1) | 1].r - T[(i << 1) | 1].l + 1); T[i << 1].lazy += T[i].lazy; T[(i << 1) | 1].lazy += T[i].lazy; T[i].lazy = 0; } } void updata(int l , int r , int ad , int i) { int mid = (T[i].l + T[i].r) >> 1; if(T[i].l == l && T[i].r == r) { T[i].sum += ad * (r - l + 1); T[i].lazy += ad; return ; } pushdown(i); if(mid < l) { updata(l , r , ad , (i << 1) | 1); } else if(mid >= r) { updata(l , r , ad , i << 1); } else { updata(l , mid , ad , i << 1) , updata(mid + 1 , r , ad , (i << 1) | 1); } T[i].sum = T[i << 1].sum + T[(i << 1) | 1].sum; } int query(int i , int pos) { int mid = (T[i].l + T[i].r) >> 1; if(T[i].l == pos && T[i].r == pos) { return T[i].sum; } pushdown(i); T[i].sum = T[i << 1].sum + T[(i << 1) | 1].sum; if(mid < pos) { return query((i << 1) | 1 , pos); } else { return query(i << 1 , pos); } } void change(int u , int v , int ad) { int f1 = top[u] , f2 = top[v]; while(f1 != f2) { if(deep[f1] < deep[f2]) { swap(f1 , f2); swap(u , v); } updata(p[f1] , p[u] , ad , 1); u = fa[f1] , f1 = top[u]; } if(deep[u] < deep[v]) swap(u , v); updata(p[v] , p[u] , ad , 1); }//树链剖分特别的更新方法,类似于lca从各子链上查询直到到了同一条重链上自行理解一下。 int main() { int n , m , q , x , y , z; while(scanf("%d%d%d" , &n , &m , &q) != EOF) { init(); for(int i = 1 ; i <= n ; i++) { scanf("%d" , &a[i]); } for(int i = 0 ; i < m ; i++) { scanf("%d%d" , &x , &y); add(x , y); add(y , x); } dfs1(1 , 0 , 0); getpos(1 , 1); char cp[5]; build(0 , pos + 1 , 1); while(q--) { scanf("%s" , cp); if(cp[0] == 'I') { scanf("%d%d%d" , &x , &y , &z); change(x , y , z); } if(cp[0] == 'D') { scanf("%d%d%d" , &x , &y , &z); change(x , y , -z); } if(cp[0] == 'Q') { scanf("%d" , &x); printf("%d " , query(1 , p[x])); } } } return 0; }