[BZOJ3589]动态树(树链剖分+dfs序+lca)
2016-09-24 07:34
274 查看
题目描述
传送门题解
修改的操作直接做就可以了。查询的时候问题主要出在会有重复的路径。我的思路就是将重复的路径砍断,变成不重复的,然后最后依次查询就可以了。由于k比较小,O(k2)判断就是可以接受的。由于树枝全部是从某个节点到根的路径的一段,处理起来就比较方便了。处理两条树枝的方法是:设两条树枝(x1,y1)(x2,y2),且h[x1]<h[y1],h[x2]<h[y2],h[x1]<h[x2]。首先求出r=lca(y1,y2),yy(为r的一个儿子,且满足y2在yy的子数中)。若h[x1]>h[r]或者h[x2]>h[r],则说明两条树枝不相交,可以不作任何处理。否则的话,使y1=yy。注意判断空树枝的情况。
这种方法只会把一条树枝砍得更短,而不会变长,所以不用担心和原先判断过的冲突。k2枚举一下就行了。
说一些写的时候出现的问题:
①做lca的时候某些时候需要将xy交换,但是交换了之后求出的两个儿子就对应不起来了。所以为了方便起见,按照y的深度排了一下序。
②这道题让我更深入地了解了lca。其实lca刚开始的时候一直向上蹦,它的原理是任意一个数都可以用二进制分解为至多logn个数相加,并且这样的话保证了x最后蹦到和y相同的高度。但是如果这样直接记录儿子的话是不行的,因为有可能最后蹦的不是一步。解决方法是加一个限制条件,y在蹦的过程中不能直接蹦到x的高度,但是最后要一步一步蹦上去。注意最后一定要让y蹦到和x相同的高度,否则后面的处理会有问题。
代码
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define N 200005 #define sz 18 #define LL long long const LL Mod=2147483648LL; int n,q,x,y,opt,dfs_clock,pt,add,k; int tot,point ,nxt[N*2],v[N*2]; int father ,size ,son ,h ,top ,in ,out ,f [sz+5],mi[sz+5]; LL sum[N*4],delta[N*4],ans; struct hp{int x,y;}edge[10]; struct hq{int fa,xx,yy;}; void addedge(int x,int y) { ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; } void dfs_1(int x,int fa,int dep) { size[x]=1; h[x]=dep; father[x]=fa; for (int i=1;i<sz;++i) { if (h[x]-mi[i]<1) break; f[x][i]=f[f[x][i-1]][i-1]; } for (int i=point[x];i;i=nxt[i]) if (v[i]!=fa) { f[v[i]][0]=x; dfs_1(v[i],x,dep+1); size[x]+=size[v[i]]; if (size[son[x]]<size[v[i]]) son[x]=v[i]; } } void dfs_2(int x,int fa) { if (son[fa]!=x) top[x]=x; else top[x]=top[fa]; in[x]=++dfs_clock; if (son[x]) dfs_2(son[x],x); for (int i=point[x];i;i=nxt[i]) if (v[i]!=fa&&v[i]!=son[x]) dfs_2(v[i],x); out[x]=dfs_clock; } hq lca(int x,int y) { hq ans; int xx=x,yy=y; for (int i=sz-1;i>=0;--i) while (h[f[x][i]]>=h[y]&&f[x][i]!=y) xx=x,x=f[x][i]; while (h[f[x][0]]>=h[y]) xx=x,x=f[x][0]; if (x==y) return ans=(hq){x,xx,yy}; for (int i=sz-1;i>=0;--i) if (f[x][i]!=f[y][i]) yy=y,xx=x,x=f[x][i],y=f[y][i]; return ans=(hq){f[x][0],x,y}; } void update(int now) { sum[now]=(sum[now<<1]+sum[now<<1|1])%Mod; } void pushdown(int now,int l,int r,int mid) { if (delta[now]) { sum[now<<1]=(sum[now<<1]+delta[now]*(mid-l+1))%Mod; sum[now<<1|1]=(sum[now<<1|1]+delta[now]*(r-mid))%Mod; delta[now<<1]=(delta[now<<1]+delta[now])%Mod; delta[now<<1|1]=(delta[now<<1|1]+delta[now])%Mod; delta[now]=0; } } void interval_change(int now,int l,int r,int lrange,int rrange,LL v) { int mid=(l+r)>>1; if (lrange<=l&&r<=rrange) { sum[now]=(sum[now]+v*(r-l+1)%Mod)%Mod; delta[now]=(delta[now]+v)%Mod; return; } pushdown(now,l,r,mid); if (lrange<=mid) interval_change(now<<1,l,mid,lrange,rrange,v); if (mid+1<=rrange) interval_change(now<<1|1,mid+1,r,lrange,rrange,v); update(now); } LL query(int now,int l,int r,int lrange,int rrange) { int mid=(l+r)>>1; LL ans=0; if (lrange<=l&&r<=rrange) return sum[now]; pushdown(now,l,r,mid); if (lrange<=mid) ans=(ans+query(now<<1,l,mid,lrange,rrange))%Mod; if (mid+1<=rrange) ans=(ans+query(now<<1|1,mid+1,r,lrange,rrange))%Mod; return ans; } LL ask(int u,int t) { LL ans=0; int f1=top[u],f2=top[t]; while (f1!=f2) { if (h[f1]<h[f2]) { swap(f1,f2); swap(u,t); } ans=(ans+query(1,1,n,in[f1],in[u]))%Mod; u=father[f1]; f1=top[u]; } if (in[u]>in[t]) swap(u,t); ans=(ans+query(1,1,n,in[u],in[t]))%Mod; return ans; } int cmp(hp a,hp b) { return h[a.y]>h[b.y]; } int main() { mi[0]=1; for (int i=1;i<=sz;++i) mi[i]=mi[i-1]*2; scanf("%d",&n); for (int i=1;i<n;++i) { scanf("%d%d",&x,&y); addedge(x,y); } dfs_1(1,0,1); dfs_2(1,0); scanf("%d",&q); for (int i=1;i<=q;++i) { scanf("%d",&opt); if (opt==0) { scanf("%d%lld",&pt,&add); interval_change(1,1,n,in[pt],out[pt],add); } else { scanf("%d",&k); for (int i=1;i<=k;++i) { scanf("%d%d",&edge[i].x,&edge[i].y); if (h[edge[i].x]>h[edge[i].y]) swap(edge[i].x,edge[i].y); } sort(edge+1,edge+k+1,cmp); for (int i=1;i<k;++i) for (int j=i+1;j<=k;++j) { int x1=edge[i].x,y1=edge[i].y,x2=edge[j].x,y2=edge[j].y; if (!x1||!x2||!y1||!y2) continue; hq r=lca(y1,y2); if (h[x1]>h[r.fa]||h[x2]>h[r.fa]) continue; if (h[x1]<h[x2]) { if (r.yy==r.fa) edge[j].x=edge[j].y=0; else edge[j].x=r.yy; } else { if (r.xx==r.fa) edge[i].x=edge[i].y=0; else edge[i].x=r.xx; } } ans=0; for (int i=1;i<=k;++i) if (edge[i].x&&edge[i].y) ans=(ans+ask(edge[i].x,edge[i].y))%Mod; printf("%lld\n",ans); } } }
总结
①lca等基础算法掌握不扎实。不是不会写,而是对原理认识的不是很清楚。②考虑问题不全面。刚开始的树枝还会加长。以后一定要想明白,判断清楚没有可能的错误再写。
相关文章推荐
- 【BZOJ3589】动态树 树链剖分+线段树
- BZOJ 3730: 震波 动态树分治 线段树 lca
- BZOJ3589: 动态树
- BZOJ 3589 动态树 树链剖分+容斥定理
- 【BZOJ】3589 动态树 树链剖分+线段树
- [BZOJ3589]动态树(树链剖分)
- bzoj3589 动态树
- BZOJ 3589 动态树
- BZOJ3589 : 动态树
- 【BZOJ 3589】 动态树
- BZOJ 3589 动态树 树链剖分+容斥原理
- bzoj千题计划214:bzoj3589: 动态树
- bzoj 3589 动态树
- bzoj3589 动态树
- 树链剖分 BZOJ3589 动态树
- 【bzoj3589】动态树 树链剖分+线段树
- BZOJ 3589 动态树 树链拆分+纳入和排除定理
- bzoj3589 动态树
- bzoj3589 动态树
- BZOJ3626: [LNOI2014]LCA