题目链接:BZOJ1036
【分析】树链剖分模版题,第一次写树链剖分,以前一直没敢学,其实很简单;我从这里学的树链剖分详解;然后有一点要注意下,网上很多都直接说树链剖分用线段树的复杂度是logn的,其实单点修改是logn没错,两点之间的查询修改是logn*logn的。
【AC CODE】2492ms
#include <cstdio> #include <cstring> #include <cctype> #include <cmath> #include <set> //#include <unordered_set> #include <queue> #include <stack> #include <vector> #include <string> #include <algorithm> using namespace std; typedef long long LL; #define rep(i,a,n) for(int i = a; i < n; i++) #define repe(i,a,n) for(int i = a; i <= n; i++) #define per(i,n,a) for(int i = n; i >= a; i--) #define clc(a,b) memset(a,b,sizeof(a)) const int INF = 0x3f3f3f3f, MAXN = 30000+10, MAXM = 30000*2+10; int head[MAXN], tol, nxt[MAXM], to[MAXM]; inline void add_edge(int u, int v) { nxt[tol] = head[u], to[tol] = v; head[u] = tol++; } /*树链剖分初始化*/ int fa[MAXN],dep[MAXN],sz[MAXN],son[MAXN], cnt,top[MAXN],tree[MAXN],pre[MAXN]; void dfs1(int u) { int num = 0; sz[u] = 1; for(int i = head[u]; ~i; i = nxt[i]) { int v = to[i]; if(v == fa[u]) continue; fa[v] = u; dep[v] = dep[u]+1; dfs1(v); if(sz[v] > num) num = sz[v], son[u] = v; sz[u] += sz[v]; } } void dfs2(int u, int num) { tree[u] = cnt, pre[cnt++] = u; top[u] = num; if(-1 == son[u]) return; dfs2(son[u],num); for(int i = head[u]; ~i; i = nxt[i]) { int v = to[i]; if(v == fa[u]) continue; if(son[u] != v) dfs2(v,v); } } /*线段树初始化*/ int mx[MAXN<<1], sum[MAXN<<1], a[MAXN]; inline int id(int x, int y){return x+y|x!=y;} void push_up(int x, int y, int m) { int u = id(x,y), l = id(x,m), r = id(m+1,y); mx[u] = max(mx[l], mx[r]); sum[u] = sum[l]+sum[r]; } void bulid(int x, int y) { if(x == y) { mx[id(x,y)] = sum[id(x,y)] = a[pre[x]]; return; } int m = (x+y)>>1; bulid(x,m); bulid(m+1,y); push_up(x,y,m); } void init(int rt) { clc(son,-1); dep[rt] = 0, fa[rt] = -1; dfs1(rt); cnt = 0; dfs2(rt,rt); bulid(0,cnt-1); } /*查询修改*/ void update(int x, int y, int p, int v)//线段树单点修改 { if(x == y) { mx[id(x,y)] = sum[id(x,y)] = v; return; } int m = (x+y)>>1; if(p <= m) update(x,m,p,v); else update(m+1,y,p,v); push_up(x,y,m); } int query_mx(int x, int y, int ql, int qr)//线段树查询区间最大值 { if(ql <= x && y <= qr) return mx[id(x,y)]; int m = (x+y)>>1, ans = -INF; if(ql <= m) ans = max(ans, query_mx(x,m,ql,qr)); if(qr > m) ans = max(ans, query_mx(m+1,y,ql,qr)); return ans; } int query_sum(int x, int y, int ql, int qr)//线段树查询区间和 { if(ql <= x && y <= qr) return sum[id(x,y)]; int m = (x+y)>>1, ans = 0; if(ql <= m) ans += query_sum(x,m,ql,qr); if(qr > m) ans += query_sum(m+1,y,ql,qr); return ans; } void tcp_update(int x, int v)//树链修改点x(x是树上点编号)的值为v { update(0,cnt-1,tree[x],v); } int tcp_mx(int x, int y)//树链查询最大值 { int f1 = top[x], f2 = top[y], ans = -INF; while(f1 != f2) { if(dep[f1] < dep[f2]) swap(f1,f2),swap(x,y); ans = max(ans, query_mx(0,cnt-1,tree[f1],tree[x])); x = fa[f1];f1 = top[x]; } x = tree[x], y = tree[y]; if(x > y) swap(x,y); ans = max(ans, query_mx(0,cnt-1,x,y)); return ans; } int tcp_sum(int x, int y)//树链查询两点之和 { int f1 = top[x], f2 = top[y], ans = 0; while(f1 != f2) { if(dep[f1] < dep[f2]) swap(f1,f2),swap(x,y); ans += query_sum(0,cnt-1,tree[f1],tree[x]); x = fa[f1];f1 = top[x]; } x = tree[x], y = tree[y]; if(x > y) swap(x,y); ans += query_sum(0,cnt-1,x,y); return ans; } int main() { #ifdef SHY freopen("d:\\1.txt", "r", stdin); #endif int n; scanf("%d%*c", &n); clc(head,-1); tol = 0; rep(i,1,n) { int u,v; scanf("%d %d%*c", &u, &v); add_edge(u,v); add_edge(v,u); } repe(i,1,n) scanf("%d%*c", &a[i]); init(1); int q; scanf("%d%*c",&q); char op[20]; while(q--) { int x,y; scanf("%s %d %d%*c", op, &x, &y); if('C' == op[0]) tcp_update(x,y); else { if('M' == op[1]) printf("%d\n", tcp_mx(x,y)); else printf("%d\n", tcp_sum(x,y)); } } return 0; }