BZOJ1036 樹的統計(樹鏈剖分+線段樹)
阿新 • • 發佈:2018-12-13
【題目描述】
一棵樹上有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
【題目分析】
這個題大意就是:給定一棵樹,要求其支援單點修改,路徑查詢最大值和,路徑求和。
這個就是典型樹鏈剖分板題了,用線段樹維護重鏈資訊,對於單點修改就直接線上段樹中修改,對於路徑詢問直接線上段樹上進行區間查詢即可。(求路過大佬指教為何我的程式WA了QAQ)
【程式碼(emmm,等我檢查出來就更新)】
#include<bits/stdc++.h> using namespace std; const int MAXN=3e4+10; const int MAXM=6e4+10; const int INF=0x3f3f3f3f; int n,q,cnt,sz; int head[MAXN],depth[MAXN],fat[MAXN],son[MAXN],siz[MAXN]; int top[MAXN],dfn[MAXN],tot,rec[MAXN]; int to[MAXM],nxt[MAXM]; int pos[MAXN],bl[MAXN]; int v[MAXN]; void Add(int x,int y) { cnt++; nxt[cnt]=head[x]; head[x]=cnt; to[cnt]=y; } void add(int x,int y) { Add(x,y); Add(y,x); } void init() { scanf("%d",&n); for(int i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y); add(x,y); } for(int i=1;i<=n;++i) scanf("%d",&v[i]); } void dfs1(int x,int fa) { if(x==1) depth[x]=1; else depth[x]=depth[fa]+1; siz[x]=1; for(int i=head[x];i!=-1;i=nxt[i]) { int v=to[i]; if(v!=fa) { fat[v]=x; dfs1(v,x); siz[x]+=siz[v]; if(siz[v]>son[x]) son[x]=v; } } } void dfs2(int x,int fa) { dfn[x]=++tot; rec[tot]=x; if(x!=1) { if(x==son[fa]) top[x]=top[fat[x]]; else top[x]=x; } for(int i=head[x];i!=-1;i=nxt[i]) { int v=to[i]; if(v!=fa) dfs2(v,x); } } int sum[MAXN<<2],maxx[MAXN<<2]; void push_up(int root) { sum[root]=sum[root<<1]+sum[root<<1|1]; maxx[root]=max(maxx[root<<1],maxx[root<<1|1]); } void build(int root,int l,int r) { if(l==r) { sum[root]=maxx[root]=v[rec[l]]; return ; } int mid=l+r>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); push_up(root); } void update(int root,int l,int r,int id,int key) { if(l==r&&l==id) { sum[root]=maxx[root]=key; return ; } int mid=l+r>>1; if(id<=mid) update(root<<1,l,mid,id,key); else update(root<<1|1,mid+1,r,id,key); push_up(root); } int querysum(int root,int l,int r,int L,int R) { if(L<=l&&r<=R) return sum[root]; int mid=l+r>>1,ret=0; if(L<=mid) ret+=querysum(root<<1,l,mid,L,R); if(R>mid) ret+=querysum(root<<1|1,mid+1,r,L,R); return ret; } int querymax(int root,int l,int r,int L,int R) { if(L<=l&&r<=R) return maxx[root]; int mid=l+r>>1,ret=0; if(L<=mid) ret=max(ret,querymax(root<<1,l,mid,L,R)); if(R>mid) ret=max(ret,querymax(root<<1|1,mid+1,r,L,R)); return ret; } int findmax(int x,int y) { int f1=top[x],f2=top[y],tmp=-INF; while(f1!=f2) { if(depth[f1]<depth[f2]) swap(f1,f2),swap(x,y); tmp=max(tmp,querymax(1,1,n,dfn[f1],dfn[x])); x=fat[f1],f1=top[x]; } if(x==y) return max(tmp,querymax(1,1,n,dfn[x],dfn[x])); if(depth[x]>depth[y]) swap(x,y); return max(tmp,querymax(1,1,n,dfn[x],dfn[y])); } int findsum(int x,int y) { int f1=top[x],f2=top[y],tmp=0; while(f1!=f2) { if(depth[f1]<depth[f2]) swap(f1,f2),swap(x,y); tmp+=querysum(1,1,n,dfn[f1],dfn[x]); x=fat[f1],f1=top[x]; } if(x==y) return tmp+querysum(1,1,n,dfn[x],dfn[x]); if(depth[x]>depth[y]) swap(x,y); return tmp+querysum(1,1,n,dfn[x],dfn[y]); } int main() { memset(head,-1,sizeof(head)); init(); dfs1(1,0); dfs2(1,1); build(1,1,n); scanf("%d",&q); while(q--) { char s[20]; int x,y; scanf("%s%d%d",s,&x,&y); if(s[0]=='C') update(1,1,n,dfn[x],y); else { if(s[1]=='M') printf("%d\n",findmax(x,y)); else printf("%d\n",findsum(x,y)); } } return 0; }