BZOJ3626 [LNOI2014]LCA(树链剖分)

来源:互联网 发布:淘宝新疆昆仑雪菊 编辑:程序博客网 时间:2024/05/16 23:35

【题解】


首先考虑任意两点u,v的LCA的deep:
若将0到u路径上所有点标记,则deep[LCA(u,v)]等于从v上溯到的第一个被标记点的deep,而再往上的话一直到根,经过的点都是被标记点 
由前缀和的思想,将0到u路径上所有点权值设为1,其他点权值为0,那么deep[LCA(u,v)]等于SUM(0到v路径上的点权和)
再进一步想,deep[LCA(u1,v)]+deep[LCA(u2,v)]怎么求?
1.可以将0到v路径上的点权设为1,再分别求u1,u2到根的点权和,但这样无法优化时间,相当于暴力 
2.也可以将0到u1路径上的点权加1,再将0到u2路径上的点权加1,然后求v到根的点权和,因为每个值为1的权相当于深度的一步,是可叠加的 
按照方法2,在 将0至ui的路径上的结点权值加1 之后(ui取遍[l,r]),就可以一并求出sigma_{l<=i<=r}dep[LCA(i,z)]了 
对于q次询问,不能每次都把点权变来变去,这里再次利用叠加性与前缀和思想,将ans[l,r]拆开,转换为ans[0,r]-ans[0,l-1],求解两个子问题即可 
这样,我们可以预处理完1~n-1每个点的影响:每个点到根所经过的点的权值加1,链剖+线段树维护路径点权和 
在这个过程中,2*q个被拆开的询问离线处理即可,比如询问ans[0,x]:在处理完点x后,求它到根的路径上的权值和,并记录下这来自第几个问题就行了 

本题中的重要思想:区间查询都可以转化为前缀和的差值,还有如何将LCA问题转化为维护树上路径信息的问题


【代码】

#include<stdio.h>#include<stdlib.h>#include<vector>#define MOD 201314using namespace std;typedef long long LL;vector<int> G[50005],A[50005],N[50005];int fa[50005]={0},size[50005]={0},son[50005]={0},top[50005]={0},pos[50005]={0},p[50005]={0};LL sumv[200000]={0},addv[200000]={0},Q[50005][5]={0};int n,e=0,tot=0;LL jdz(LL x){if(x<0) x=-x;return x;}void dfs1(int x){int i;size[x]=1;for(i=0;i<G[x].size();i++){dfs1(G[x][i]);size[x]+=size[G[x][i]];if(son[x]==0||size[son[x]]<size[G[x][i]]) son[x]=G[x][i];}}void dfs2(int x,int t){int i;top[x]=t;pos[x]=++tot;if(son[x]!=0) dfs2(son[x],t);for(i=0;i<G[x].size();i++)if(G[x][i]!=son[x]) dfs2(G[x][i],G[x][i]);}void xg(LL p,int x,int y,int o,int left,int right){int mid=(left+right)/2;if(x<=left&&right<=y) addv[o]+=p;else{if(x<=mid) xg(p,x,y,o*2,left,mid);if(y>mid) xg(p,x,y,o*2+1,mid+1,right);}if(left<right) sumv[o]=sumv[o*2]+sumv[o*2+1]+addv[o]*(LL)(right-left+1);else sumv[o]+=p;}LL cx(int x,int y,int o,int left,int right,LL add){LL ans=0;int mid=(left+right)/2;if(x<=left&&right<=y) return sumv[o]+add*(LL)(right-left+1);add+=addv[o];if(x<=mid) ans+=cx(x,y,o*2,left,mid,add);if(y>mid) ans+=cx(x,y,o*2+1,mid+1,right,add);return ans;}void update(int x){int tx=top[x];while(tx!=0){xg(1,pos[tx],pos[x],1,1,n);x=fa[tx];tx=top[x];}xg(1,pos[0],pos[x],1,1,n);}LL getsum(int x){LL ans=0;int tx=top[x];while(tx!=0){ans=ans+cx(pos[tx],pos[x],1,1,n,0);x=fa[tx];tx=top[x];}return ans+cx(pos[0],pos[x],1,1,n,0);}int main(){int q,i,j,l,r,z;scanf("%d%d",&n,&q);for(i=1;i<n;i++){scanf("%d",&fa[i]);G[fa[i]].push_back(i);}dfs1(0);dfs2(0,0);for(i=1;i<=q;i++){scanf("%d%d%d",&l,&r,&z);if(l>0){A[l-1].push_back(z);N[l-1].push_back(i);}A[r].push_back(z);N[r].push_back(i);}for(i=0;i<n;i++){update(i);for(j=0;j<A[i].size();j++)Q[N[i][j]][++p[N[i][j]]]=getsum(A[i][j]);}for(i=1;i<=q;i++)printf("%lld\n",jdz(Q[i][1]-Q[i][2])%MOD);return 0;}
注意最后求答案时Q[i][j]不能是mod过的,否则会导致错误,所以Q[i][j]需定义成long long类型

或者Q[i][j]是mod过的也行,但需要记录这个Q[i][j]属于ans[0,l-1]还是ans[0,r]

0 0
原创粉丝点击