[BZOJ3626][LNOI2014]LCA(离线+链剖)
2017-01-11 16:50
393 查看
=== ===
这里放传送门=== ===
题解
感觉这题思路还是比较神的。。对于一个点u,我们要查它跟某个给定点z的LCA的深度,那么如果我们把u这个点到根节点的路径全都打上+1的标记,那么z到根的路径上的标记数目就是u和z的LCA的深度。这个东西画一画就感觉比较科学了。。如果是对于两个点u和v要求跟z的LCA的深度,那就把u和v都打上到根的一串标记然后查z到根的标记个数就可以了。。。
那我们可以离线询问,离线完了以后就可以把每个询问拆成两个,因为这个东西显然是满足前缀和相减的性质。那问题就变成了有一堆[1,n]这样的区间要求回答这个询问。结合上面那种方法,我们可以把询问们都按照右端点从小到大排序,然后从1号点开始一个一个往树上打一串标记。每次当走到i这个点的时候说明1-i所有点的标记已经都被加进去了,那么这个时候就可以处理[1,i]这样的询问了。
代码
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,Q,p[50010],a[50010],next[50010],top[50010],size[50010],son[50010],fa[50010],tot,cnt; int deep[50010],sum[200010],dlt[200010],ans[50010][2],cur[50010],ptr,w[50010],qcnt; void add(int x,int y){ tot++;a[tot]=y;next[tot]=p[x];p[x]=tot; } struct question{ int r,z,id,iid; }q[100010]; int comp(question a,question b){ return a.r<b.r; } void addquery(int r,int z,int id,int iid){ ++qcnt;q[qcnt].r=r;q[qcnt].z=z;q[qcnt].id=id;q[qcnt].iid=iid; } void dfs(){ int u=1; bool flag; while (true){ flag=false; if (deep[u]==0){ deep[u]=deep[fa[u]]+1; size[u]=1;son[u]=0;cur[u]=p[u]; } for (int i=cur[u];i!=0;i=next[i]){ int v=a[i]; cur[u]=next[i];fa[v]=u; u=v;flag=true;break; } if (flag==false) if (u==1) break; else{ int v=fa[u]; size[v]+=size[u]; if (size[son[v]]<size[u]) son[v]=u; u=fa[u]; } } } void dfs_again(){ int u=1,tp=1; bool flag; while (true){ flag=false; if (top[u]==0){ top[u]=tp;w[u]=++cnt;cur[u]=p[u]; if (son[u]!=0){u=son[u];continue;} } for (int i=cur[u];i!=0;i=next[i]){ cur[u]=next[i];u=a[i]; tp=a[i];flag=true;break; } if (flag==false) if (u==1) break; else u=fa[u]; } } void update(int i){ sum[i]=sum[i<<1]+sum[(i<<1)+1]; } void pushdown(int i,int l,int r){ if (dlt[i]!=0){ int mid=(l+r)>>1; sum[i<<1]+=(mid-l+1)*dlt[i]; sum[(i<<1)+1]+=(r-mid)*dlt[i]; dlt[i<<1]+=dlt[i];dlt[(i<<1)+1]+=dlt[i]; dlt[i]=0; } } void add(int i,int l,int r,int left,int right){ if (left<=l&&right>=r){ sum[i]+=r-l+1;dlt[i]++; return; } int mid=(l+r)>>1; pushdown(i,l,r); if (left<=mid) add(i<<1,l,mid,left,right); if (right>mid) add((i<<1)+1,mid+1,r,left,right); update(i); } void change(int x,int y){ while (top[x]!=top[y]){ if (deep[top[x]]<deep[top[y]]) swap(x,y); add(1,1,n,w[top[x]],w[x]); x=fa[top[x]]; } if (deep[x]>deep[y]) swap(x,y); add(1,1,n,w[x],w[y]); } int ask(int i,int l,int r,int left,int right){ if (left<=l&&right>=r) return sum[i]; int mid=(l+r)>>1,ans=0; pushdown(i,l,r); if (left<=mid) ans+=ask(i<<1,l,mid,left,right); if (right>mid) ans+=ask((i<<1)+1,mid+1,r,left,right); return ans; } int query(int x,int y){ int ans=0; while (top[x]!=top[y]){ if (deep[top[x]]<deep[top[y]]) swap(x,y); ans+=ask(1,1,n,w[top[x]],w[x]); x=fa[top[x]]; } if (deep[x]>deep[y]) swap(x,y); ans+=ask(1,1,n,w[x],w[y]); return ans; } int main() { scanf("%d%d",&n,&Q); for (int i=2;i<=n;i++){ int fa;scanf("%d",&fa);++fa; add(fa,i); } dfs();dfs_again(); for (int i=1;i<=Q;i++){ int l,r,z; scanf("%d%d%d",&l,&r,&z); ++l;++r;++z; addquery(l-1,z,i,0); addquery(r,z,i,1);//把每个询问拆成两个 } sort(q+1,q+qcnt+1,comp); ptr=1; while (q[ptr].r==0){ ans[q[ptr].id][q[ptr].iid]=0; ++ptr; } for (int i=1;i<=n;i++){ change(1,i); while (q[ptr].r==i){ ans[q[ptr].id][q[ptr].iid]=query(1,q[ptr].z); ++ptr; } } for (int i=1;i<=Q;i++) printf("%d\n",(ans[i][1]-ans[i][0])%201314); return 0; }
偏偏在最后出现的补充说明
离线处理有时候往往可以利用某种单调性,通过动态维护来放宽单个操作的时限。相关文章推荐
- BZOJ3626 : [LNOI2014]LCA
- bzoj 3626: [LNOI2014]LCA
- bzoj3626: [LNOI2014]LCA 树链剖分
- BZOJ 3626 [LNOI2014]LCA ——树链剖分
- BZOJ3626 [LNOI2014]LCA(树链剖分)
- 【bzoj3626】[LNOI2014]LCA
- bzoj3626[LNOI2014] LCA
- [bzoj3626][LNOI2014]LCA
- bzoj3626【LNOI2014】LCA
- BZOJ 3626: [LNOI2014]LCA
- [bzoj3626][LNOI2014]LCA 树链剖分
- BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)
- AC日记——[LNOI2014]LCA bzoj 3626
- BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
- 【BZOJ3626】[LNOI2014]LCA 离线+树链剖分+线段树
- [BZOJ3626][LNOI2014]LCA(离线+差分+树链剖分)
- bzoj 3626: [LNOI2014]LCA 离线+树链剖分
- 【BZOJ 3626】 [LNOI2014]LCA【在线+主席树+树剖】
- bzoj3626 [LNOI2014]LCA