bzoj1036: [ZJOI2008]樹的統計Count(樹鏈剖分+線段樹維護)
bzoj1036: [ZJOI2008]樹的統計Count
Time Limit: 10 Sec
Memory Limit: 162 MBDescription
一棵樹上有n個節點,編號分別為1到n,每個節點都有一個權值w。我們將以下面的形式來要求你對這棵樹完成一些操作:
I. CHANGE u t : 把結點u的權值改為t II. QMAX u v: 詢問從點u到點v的路徑上的節點的最大權值 I
II. QSUM u v: 詢問從點u到點v的路徑上的節點的權值和 註意:從點u到點v的路徑上的節點包括u和v本身
Input
輸入的第一行為一個整數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之間。
Output
對於每個“QMAX”或者“QSUM”的操作,每行輸出一個整數表示要求輸出的結果。
Sample Input
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 4Sample Output
4
1
2
2
10
6
5
6
5
16
題目地址: bzoj1036: [ZJOI2008]樹的統計Count
題目大意:
題目已經很清楚了
題解:
樹鏈剖分裸題
先把樹輕重鏈剖分(兩遍dfs)
然後線段樹維護區間最大值和區間和
將剖出來的序列合並一下就好了
AC代碼
#include <cstdio> #include <cstring> #include <algorithm> #define inf 0x7fffffff #define N 30005 using namespace std; int n,Q,cnt,sz; int w[N],dep[N],size[N],head[N],fa[N]; int pos[N],top[N]; char ch[10]; struct edge{ int to,next; }e[N+N]; struct seg{ int l,r,mx,sum; }t[N<<2]; void add_edge(int u,int v){ e[++cnt]=(edge){v,head[u]};head[u]=cnt; e[++cnt]=(edge){u,head[v]};head[v]=cnt; } void dfs1(int u){ size[u]=1; for(int i=head[u];i;i=e[i].next){ if(e[i].to==fa[u])continue; dep[e[i].to]=dep[u]+1; fa[e[i].to]=u; dfs1(e[i].to); size[u]+=size[e[i].to]; } } void dfs2(int u,int chain){ int k=0;sz++; pos[u]=sz; top[u]=chain; for(int i=head[u];i;i=e[i].next) if(dep[e[i].to]>dep[u]&&size[e[i].to]>size[k]) k=e[i].to; if(k==0)return; dfs2(k,chain); for(int i=head[u];i;i=e[i].next) if(dep[e[i].to]>dep[u]&&k!=e[i].to) dfs2(e[i].to,e[i].to); } void build(int l,int r,int id){ t[id].l=l;t[id].r=r; if(l==r)return; int mid=(l+r)>>1; build(l,mid,id<<1); build(mid+1,r,id<<1|1); } void change(int id,int k,int w){ int l=t[id].l,r=t[id].r,mid=(l+r)>>1; if(l==r){ t[id].sum=t[id].mx=w; return; } if(k<=mid)change(id<<1,k,w); else change(id<<1|1,k,w); t[id].sum=t[id<<1].sum+t[id<<1|1].sum; t[id].mx=max(t[id<<1].mx,t[id<<1|1].mx); } int querysum(int id,int L,int R){ int l=t[id].l,r=t[id].r,mid=(l+r)>>1; if(l==L&&R==r)return t[id].sum; if(R<=mid)return querysum(id<<1,L,R); else if(L>mid)return querysum(id<<1|1,L,R); else return querysum(id<<1,L,mid)+querysum(id<<1|1,mid+1,R); } int querymx(int id,int L,int R){ int l=t[id].l,r=t[id].r,mid=(l+r)>>1; if(l==L&&R==r)return t[id].mx; if(R<=mid)return querymx(id<<1,L,R); else if(L>mid)return querymx(id<<1|1,L,R); else return max(querymx(id<<1,L,mid),querymx(id<<1|1,mid+1,R)); } int solvesum(int a,int b){ int sum=0; while(top[a]!=top[b]){ if(dep[top[a]]<dep[top[b]])swap(a,b); sum+=querysum(1,pos[top[a]],pos[a]); a=fa[top[a]]; } if(pos[a]>pos[b])swap(a,b); sum+=querysum(1,pos[a],pos[b]); return sum; } int solvemx(int a,int b){ int mx=-inf; while(top[a]!=top[b]){ if(dep[top[a]]<dep[top[b]])swap(a,b); mx=max(mx,querymx(1,pos[top[a]],pos[a])); a=fa[top[a]]; } if(pos[a]>pos[b])swap(a,b); mx=max(mx,querymx(1,pos[a],pos[b])); return mx; } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); add_edge(u,v); } for(int i=1;i<=n;i++)scanf("%d",&w[i]); dfs1(1); dfs2(1,1); build(1,n,1); for(int i=1;i<=n;i++) change(1,pos[i],w[i]); scanf("%d",&Q); while(Q--){ int x,y;scanf("%s%d%d",ch+1,&x,&y); if(ch[1]=='C'){ w[x]=y; change(1,pos[x],y); }else if(ch[2]=='M') printf("%d\n",solvemx(x,y)); else printf("%d\n",solvesum(x,y)); } return 0; }
bzoj1036: [ZJOI2008]樹的統計Count(樹鏈剖分+線段樹維護)