【ZJOI2008】树的统计
一道普通的树链剖分模板题,注意细节即可
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 typedef long long ll; 5 inline int read() { 6 int ret=0,f=1; 7 char c=getchar(); 8 while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} 9 while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar(); 10 return ret*f; 11 } 12 using namespace std; 13 int n,q; 14 struct edge { 15 int next,to; 16 }a[30010<<1]; 17 int head[30010<<1],num,val[30010]; 18 int fa[30010],size[30010],d[30010],son[30010],top[30010]; 19 int seg[30010],rev[30010],sum[30010<<2],maxx[30010<<2]; 20 int retsum,retmax; 21 char in[11]; 22 inline void add(int from,int to) { 23 a[++num].next=head[from]; a[num].to=to; head[from]=num; 24 swap(from,to); 25 a[++num].next=head[from]; a[num].to=to; head[from]=num; 26 } 27 inline void pushup(int now) { 28 sum[now]=sum[now<<1]+sum[now<<1|1]; 29 maxx[now]=max(maxx[now<<1],maxx[now<<1|1]); 30 } 31 void dfs1(int u,int f) { 32 size[u]=1; 33 fa[u]=f; 34 d[u]=d[f]+1; 35 for(int i=head[u];i;i=a[i].next) { 36 int v=a[i].to; 37 if(v==f) continue ; 38 dfs1(v,u); 39 size[u]+=size[v]; 40 if(size[v]>size[son[u]]) son[u]=v; 41 } 42 } 43 void dfs2(int u,int f) { 44 if(son[u]) { 45 seg[son[u]]=++seg[0]; 46 top[son[u]]=top[u]; 47 rev[seg[0]]=son[u]; 48 dfs2(son[u],u); 49 } 50 for(int i=head[u];i;i=a[i].next) { 51 int v=a[i].to; 52 if(!top[v]) { 53 seg[v]=++seg[0]; 54 rev[seg[0]]=v; 55 top[v]=v; 56 dfs2(v,u); 57 } 58 } 59 } 60 void build(int now,int l,int r) { 61 if(l==r) { 62 sum[now]=maxx[now]=val[rev[l]]; 63 return ; 64 } 65 int mid=l+r>>1; 66 build(now<<1,l,mid); 67 build(now<<1|1,mid+1,r); 68 pushup(now); 69 } 70 void updata(int now,int l,int r,int x,int v) { 71 if(l==r) { 72 sum[now]=v; 73 maxx[now]=v; 74 return ; 75 } 76 int mid=l+r>>1; 77 if(x<=mid) updata(now<<1,l,mid,x,v); 78 else updata(now<<1|1,mid+1,r,x,v); 79 pushup(now); 80 } 81 void query(int now,int l,int r,int x,int y) { 82 if(x<=l&&r<=y) { 83 retsum+=sum[now]; 84 retmax=max(retmax,maxx[now]); 85 return ; 86 } 87 int mid=l+r>>1; 88 if(x<=mid) query(now<<1,l,mid,x,y); 89 if(y>mid) query(now<<1|1,mid+1,r,x,y); 90 } 91 void find(int x,int y) { 92 while(top[x]!=top[y]) { 93 if(d[top[x]]<d[top[y]]) swap(x,y); 94 query(1,1,seg[0],seg[top[x]],seg[x]); 95 x=fa[top[x]]; 96 } 97 if(d[x]>d[y]) swap(x,y); 98 query(1,1,seg[0],seg[x],seg[y]); 99 } 100 int main() { 101 n=read(); 102 for(int i=1;i<n;i++) add(read(),read()); 103 for(int i=1;i<=n;i++) val[i]=read(); 104 dfs1(1,0); 105 seg[0]=seg[1]=top[1]=rev[1]=1; 106 dfs2(1,0); 107 build(1,1,seg[0]); 108 q=read(); 109 while(q--) { 110 scanf("%s",in+1); 111 int x=read(),y=read(); 112 if(in[1]=='C') updata(1,1,seg[0],seg[x],y); 113 else { 114 retsum=0; 115 retmax=-999999999; 116 find(x,y); 117 if(in[2]=='M') printf("%d\n",retmax); 118 else printf("%d\n",retsum); 119 } 120 } 121 return 0; 122 }
几个易错点总结:
- 树链剖分的求解过程实质上是线段树,所以线段树维护的相关数组应开4倍空间
- 注意区分seg与rev的含义
- retmax,retsum赋初值
- 注意第95行(个人易错)