【块状树】【树链剖分】bzoj1036 [ZJOI2008]树的统计Count
2014-09-15 18:35
429 查看
很早之前用树链剖分写过,但是代码太长太难写,省选现场就写错了。
学了个块状树,好写不少,而且常数较小,比链剖慢不了多少。
在dfs时分块,只要当前块满了sqrt(n),就分新的一块。
对每个点,维护从这个点到该块的根(top)的路径上的答案。
更新的时候,只会对 该点 在该块内的子树 造成影响。
询问时,暴力LCA。
这样更新和询问都是O(sqrt(n))的。
Orz zky。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define lson rt<<1,l,m #define rson rt<<1|1,m+1,r #define maxn 60000 int n,m,u,v; int V[maxn],Next[maxn],First[maxn]; int Val[maxn]; int fa[maxn],dep[maxn],son[maxn],siz[maxn],top[maxn],Num[maxn],tot,en; int maxv[maxn<<2],sumv[maxn<<2]; bool vis[maxn]; char s[7]; inline void AddEdge(int UU,int VV) { V[++en]=VV; Next[en]=First[UU]; First[UU]=en; } int query_sum(int ql,int qr,int rt,int l,int r) { if(ql<=l&&r<=qr) return sumv[rt]; int m=l+r>>1,ans=0; if(ql<=m) ans+=query_sum(ql,qr,lson); if(m<qr) ans+=query_sum(ql,qr,rson); return ans; } int query_max(int ql,int qr,int rt,int l,int r) { if(ql<=l&&r<=qr) return maxv[rt]; int m=l+r>>1,ans=-2147483647; if(ql<=m) ans=max(ans,query_max(ql,qr,lson)); if(m<qr) ans=max(ans,query_max(ql,qr,rson)); return ans; } void update(int p,int v,int rt,int l,int r) { int m=l+r>>1; if(l==r) { maxv[rt]=sumv[rt]=v; return; } if(p<=m) update(p,v,lson); else update(p,v,rson); sumv[rt]=sumv[rt<<1]+sumv[(rt<<1)+1]; maxv[rt]=max(maxv[rt<<1],maxv[(rt<<1)+1]); } inline void Change(int p,int v) { update(p,v,1,1,n); } inline int Query_sum(int u,int v) { int f1=top[u],f2=top[v],res=0; while(f1!=f2) { if(dep[f1]<dep[f2]) { swap(u,v); swap(f1,f2); } res+=query_sum(Num[f1],Num[u],1,1,n); u=fa[f1]; f1=top[u]; } if(dep[u]>dep[v]) swap(u,v); return res+query_sum(Num[u],Num[v],1,1,n); } inline int Query_max(int u,int v) { int f1=top[u],f2=top[v],res=-2147483647; while(f1!=f2) { if(dep[f1]<dep[f2]) { swap(u,v); swap(f1,f2); } res=max(res,query_max(Num[f1],Num[u],1,1,n)); u=fa[f1]; f1=top[u]; } if(dep[u]>dep[v]) swap(u,v); return max(res,query_max(Num[u],Num[v],1,1,n)); } void dfs1(int cur,int father,int depth) { fa[cur]=father; dep[cur]=depth; siz[cur]=1; for(int i=First[cur];i;i=Next[i]) if(!vis[V[i]]) { vis[V[i]]=true; dfs1(V[i],cur,depth+1); siz[cur]+=siz[V[i]]; if(siz[V[i]]>siz[son[cur]]) son[cur]=V[i]; vis[V[i]]=false; } } void dfs2(int cur) { if(son[cur]&&!vis[son[cur]]) { vis[son[cur]]=true; top[son[cur]]=top[cur]; Num[son[cur]]=++tot; Change(tot,Val[son[cur]]); dfs2(son[cur]); vis[son[cur]]=false; } for(int i=First[cur];i;i=Next[i]) if(son[cur]!=V[i]&&!vis[V[i]]) { vis[V[i]]=true; top[V[i]]=V[i]; Num[V[i]]=++tot; Change(tot,Val[V[i]]); dfs2(V[i]); vis[V[i]]=false; } } int main() { scanf("%d",&n); 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",&Val[i]); top[1]=1; Num[1]=++tot; Change(tot,Val[1]); vis[1]=true; dfs1(1,0,1); dfs2(1); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s",s); if(s[1]=='H') { scanf("%d%d",&u,&v); Change(Num[u],v); } else if(s[1]=='M') { scanf("%d%d",&u,&v); printf("%d\n",Query_max(u,v)); } else { scanf("%d%d",&u,&v); printf("%d\n",Query_sum(u,v)); } } return 0; }
学了个块状树,好写不少,而且常数较小,比链剖慢不了多少。
在dfs时分块,只要当前块满了sqrt(n),就分新的一块。
对每个点,维护从这个点到该块的根(top)的路径上的答案。
更新的时候,只会对 该点 在该块内的子树 造成影响。
询问时,暴力LCA。
这样更新和询问都是O(sqrt(n))的。
Orz zky。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; struct Graph { int v[60001],first[60001],next[60001],en; void AddEdge(const int &a,const int &b) {v[++en]=b;next[en]=first[a];first[a]=en;} }; Graph G[2]; int fa[30001],dep[30001],top[30001],siz[30001],sz; int maxv[30001],sumv[30001],w[30001]; int n,x,y,q; char op[10]; void makeblock(int cur) { for(int i=G[0].first[cur];i;i=G[0].next[i]) if(G[0].v[i]!=fa[cur]) { dep[G[0].v[i]]=dep[cur]+1; fa[G[0].v[i]]=cur; if(siz[top[cur]]<sz) { siz[top[cur]]++; top[G[0].v[i]]=top[cur]; G[1].AddEdge(cur,G[0].v[i]); } makeblock(G[0].v[i]); } } void dfs(int cur,int Sumnow,int Maxnow) { maxv[cur]=Maxnow; sumv[cur]=Sumnow; for(int i=G[1].first[cur];i;i=G[1].next[i]) dfs(G[1].v[i],Sumnow+w[G[1].v[i]],max(Maxnow,w[G[1].v[i]])); } inline void update(int p,int val) { w[p]=val; if(p==top[p]) dfs(p,val,val); else dfs(p,val+sumv[fa[p]],max(val,maxv[fa[p]])); } inline int Query_max(int u,int v) { int res=-2147483647; while(u!=v) { if(top[u]==top[v]) { if(dep[u]<dep[v]) swap(u,v); res=max(res,w[u]); u=fa[u]; } else { if(dep[top[u]]<dep[top[v]]) swap(u,v); res=max(res,maxv[u]); u=fa[top[u]]; } } return max(res,w[u]); } inline int Query_sum(int u,int v) { int res=0; while(u!=v) { if(top[u]==top[v]) { if(dep[u]<dep[v]) swap(u,v); res+=w[u]; u=fa[u]; } else { if(dep[top[u]]<dep[top[v]]) swap(u,v); res+=sumv[u]; u=fa[top[u]]; } } return res+w[u]; } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); G[0].AddEdge(x,y); G[0].AddEdge(y,x); } sz=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&w[i]); top[i]=i; siz[i]=1; } makeblock(1); for(int i=1;i<=n;i++) if(top[i]==i) dfs(i,w[i],w[i]); scanf("%d",&q); for(int i=1;i<=q;i++) { scanf("%s%d%d",op,&x,&y); if(op[1]=='M') printf("%d\n",Query_max(x,y)); else if(op[1]=='H') update(x,y); else printf("%d\n",Query_sum(x,y)); } return 0; }
相关文章推荐
- 树链剖分+线段树 BZOJ 1036 [ZJOI2008]树的统计Count
- BZOJ 1036: [ZJOI2008]树的统计Count 树链剖分
- BZOJ 树链剖分 1036: [ZJOI2008]树的统计Count
- 【树链剖分模板】【bzoj1036】: [ZJOI2008]树的统计Count
- [BZOJ1036][ZJOI2008]树的统计Count
- BZOJ 1036: [ZJOI2008]树的统计Count 【树链剖分】
- BZOJ 1036: [ZJOI2008]树的统计Count (树链剖分模板题)
- BZOJ[1036] [ZJOI2008]树的统计Count 树链剖分模板
- 树链剖分基础模板(BZOJ1036[ZJOI2008]树的统计Count)
- BZOJ 1036 [ZJOI2008]树的统计Count
- bzoj 1036: [ZJOI2008]树的统计Count——树链剖分
- bzoj 1036: [ZJOI2008]树的统计Count(树链剖分)
- bzoj1036[ZJOI2008]树的统计Count 树链剖分+线段树
- BZOJ-1036: [ZJOI2008]树的统计Count (树链剖分 线段树 单点修改 区间查询 入门题)
- bzoj 1036 [ZJOI2008] 树的统计 树链剖分模板题
- BZOJ1036: [ZJOI2008]树的统计Count
- kyeremal-bzoj1036[ZJOI2008]-树的统计count-树链剖分
- BZOJ 1036 [ZJOI2008] 数的统计 树链剖分
- BZoj 1036: [ZJOI2008]树的统计Count【树链剖分+线段树--模板题】
- [BZOJ1036][ZJOI2008]树的统计Count(树链剖分)