1. 程式人生 > >bzoj1036點權模板題

bzoj1036點權模板題

/*
HYSBZ1036
樹上有1-n個結點,每個節點都有一個權值w
操作 CHANGE u t:把結點u的權值改為t
     QMAX u v:詢問從點u到v的路徑上的節點的最大權值
     QSUM u v:詢問從點u到v的路徑上的結點的權值和
從點u到點v路徑上的結點包括u,v本身
 
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXN 30010
using namespace std;
struct Edge{
    
int to, next; }edge[MAXN*2]; int head[MAXN], tot; int top[MAXN];//重鏈頂端 int fa[MAXN];//父親 int deep[MAXN];//深度,root=1 int num[MAXN];//子節點數 int p[MAXN];//v線上段樹中的位置 int fp[MAXN]; int son[MAXN];//重兒子 int pos; void init(){ tot = 0; memset(head, -1, sizeof(head)); pos = 0; memset(son, -1, sizeof(son)); }
void addedge(int u, int v){ edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } //第一次dfs求fa,deep,son,num 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].to;
if (v != pre){ dfs1(v, u, d+1); num[u]+=num[v]; if (son[u]==-1||num[v]>num[son[u]]) son[u] = v; } } } //第二次dfs求top,p,用pos記數 void getpos(int u, int sp){//sp表示重鏈頂端 top[u] = sp; p[u] = pos++; fp[p[u]] = 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].to;//輕兒子 if (v != son[u] && v != fa[u]) getpos(v, v); } } //線段樹部分 struct Node{ int l, r; int sum; int Max; }segTree[MAXN*3]; inline void push_up(int i){ segTree[i].sum = segTree[i<<1].sum + segTree[i<<1|1].sum; segTree[i].Max = max(segTree[i<<1].Max, segTree[i<<1|1].Max); } int s[MAXN]; void build(int i, int l, int r){ segTree[i].l = l; segTree[i].r = r; if (l==r){ segTree[i].sum = segTree[i].Max = s[fp[l]]; return; } int mid = l+r >> 1; build(i<<1, l, mid); build(i<<1|1, mid+1, r); push_up(i); } //更新線段樹的第k個值為val void update(int i, int k, int val){ if(segTree[i].l == k && segTree[i].r == k){ segTree[i].Max = segTree[i].sum = val; return; } int mid = segTree[i].l + segTree[i].r >> 1; if (k <= mid) update(i<<1, k, val); else update(i<<1|1, k, val); push_up(i);//單點更新後要重新計算Max和Sum } //查詢[l,r]區間的最大值 int queryMax(int i, int l, int r){ if(segTree[i].l == l && segTree[i].r == r) return segTree[i].Max; int mid = segTree[i].l+segTree[i].r >> 1; if (r <= mid) return queryMax(i<<1, l, r); else if (l > mid) return queryMax(i<<1|1, l, r); else return max(queryMax(i<<1, l, mid), queryMax(i<<1|1, mid+1, r)); } //查詢[l,r]區間的和 int querySum(int i, int l, int r){ if(segTree[i].l==l && segTree[i].r == r) return segTree[i].sum; int mid = segTree[i].l+segTree[i].r>>1; if(r <= mid) return querySum(i<<1,l,r); else if(l > mid) return querySum((i<<1)|1,l,r); else return querySum(i<<1, l, mid)+querySum(i<<1|1, mid+1, r); } //查詢u->v路徑上結點的最大權值 int findMax(int u, int v){ int f1 = top[u], f2 = top[v]; int tmp = -1000000000; while(f1!=f2){//不在同一條重鏈上時 if(deep[f1]<deep[f2]){ swap(u, v); swap(f1, f2); } tmp = max(tmp, queryMax(1, p[f1], p[u]));//把這一條鏈上的求出來 u = fa[f1]; f1 = top[u]; } if (deep[u]>deep[v]) swap(u,v); return max(tmp, queryMax(1, p[u], p[v])); } //查詢u->v路徑上結點的權值 int findSum(int u, int v){ int f1 = top[u]; int f2 = top[v]; int tmp = 0; while(f1 != f2){ if (deep[f1] < deep[f2]){ swap(f1, f2); swap(u, v); } tmp += querySum(1, p[f1], p[u]); u = fa[f1]; f1 = top[u]; } if(deep[u]>deep[v]) swap(u, v); return tmp + querySum(1, p[u], p[v]); } int main(){ int n; int q; char op[20]; int u, v; while(scanf("%d", &n)==1){ init(); for(int i = 1; i < n; i++){ scanf("%d%d", &u, &v); addedge(u, v); addedge(v, u); } for(int i = 1; i <= n; i++) scanf("%d", &s[i]); dfs1(1,0,0); getpos(1,1); build(1, 0, pos-1); scanf("%d", &q); while(q--){ scanf("%s%d%d", op, &u, &v); if (op[0]=='C') update(1, p[u], v);//單點修改 else if (strcmp(op, "QMAX")==0) printf("%d\n", findMax(u, v));//查詢u->v路徑上點權的最大值 else printf("%d\n", findSum(u, v));//查詢路徑上點權的和 } } return 0; }