【BZOJ3626】LCA(树链剖分)
来源:互联网 发布:有关程序员的个性签名 编辑:程序博客网 时间:2024/05/22 01:47
传送门
LCA
I think
%%%:( 清华爷题解 )考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] − [1, l − 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n − 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。
实现:将询问按l排序,设置“断点”后for循环扫过去中间添加while循环即可实现。
个人觉得这真是优美的一道题,而且1A很开心>-<
Code
/************************************************************** Problem: 3626 User: Etta Language: C++ Result: Accepted Time:1824 ms Memory:8332 kb****************************************************************/#include<cstdio>#include<algorithm>typedef long long LL;const int sm = 5e4+10;const int mod = 201314;int N,Q,cnt,tot;int to[sm<<1],nxt[sm<<1],hd[sm];int Fa[sm],dp[sm],sn[sm],sz[sm],tp[sm],pl[sm];LL C[sm<<2],Mk[sm<<2];struct Stop { int p,num; bool flag;}stp[sm<<1];struct Ans { int z; LL res[2];}ans[sm];void Add(int u,int v) { to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot; to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot;} bool cmp(Stop x,Stop y) { return x.p<y.p; }namespace Tree { void Dfsa(int x,int fa) { dp[x]=dp[fa]+1,sn[x]=0; sz[x]=1,Fa[x]=fa; for(int i=hd[x];i;i=nxt[i]) if(to[i]!=fa) { Dfsa(to[i],x); sz[x]+=sz[to[i]]; if(sz[sn[x]]<sz[to[i]]) sn[x]=to[i]; } } void Dfsb(int x,int top) { pl[x]=++tot,tp[x]=top; if(sn[x]) Dfsb(sn[x],top); for(int i=hd[x];i;i=nxt[i]) if(to[i]!=Fa[x]&&to[i]!=sn[x]) Dfsb(to[i],to[i]); } void Pd(int rt,int l,int r,int m) { Mk[rt<<1]+=Mk[rt],Mk[rt<<1|1]+=Mk[rt]; C[rt<<1]+=1ll*(m-l+1)*Mk[rt],C[rt<<1|1]+=1ll*(r-m)*Mk[rt]; Mk[rt]=0; } void Update(int rt,int l,int r,int a,int b) { if(a<=l&&r<=b) { C[rt]+=r-l+1,Mk[rt]++; return ; } int m=(l+r)>>1; if(Mk[rt]) Pd(rt,l,r,m); if(a<=m) Update(rt<<1,l,m,a,b); if(b> m) Update(rt<<1|1,m+1,r,a,b); C[rt]=C[rt<<1]+C[rt<<1|1]; } LL Query(int rt,int l,int r,int a,int b) { if(a<=l&&r<=b) return C[rt]; int m=(l+r)>>1;LL Ans=0; if(Mk[rt]) Pd(rt,l,r,m); if(a<=m) Ans+=Query(rt<<1,l,m,a,b); if(b> m) Ans+=Query(rt<<1|1,m+1,r,a,b); C[rt]=C[rt<<1]+C[rt<<1|1]; return Ans; } void TUpdate(int x) { while(tp[x]!=tp[1]) { Update(1,1,N,pl[tp[x]],pl[x]); x=Fa[tp[x]]; } Update(1,1,N,pl[1],pl[x]); } LL TQuery(int Z) { LL Ans=0; while(tp[Z]!=tp[1]) { Ans+=Query(1,1,N,pl[tp[Z]],pl[Z]); Z=Fa[tp[Z]]; } Ans+=Query(1,1,N,pl[1],pl[Z]); return Ans; }}int main() { using namespace Tree; scanf("%d%d",&N,&Q); for(int i=2,x;i<=N;++i) scanf("%d",&x),Add(i,x+1); for(int i=1,l,r;i<=Q;++i) { scanf("%d%d%d",&l,&r,&ans[i].z),ans[i].z++; stp[++cnt].num=i,stp[cnt].p=l,stp[cnt].flag=0; stp[++cnt].num=i,stp[cnt].p=r+1,stp[cnt].flag=1; } std::sort(stp+1,stp+cnt+1,cmp); tot=0,Dfsa(1,0),Dfsb(1,1); for(int i=1,j=1;j<=cnt;++j) { while(i<=stp[j].p) TUpdate(i),++i;//先更新后询问 ans[stp[j].num].res[stp[j].flag]=TQuery(ans[stp[j].num].z); } for(int i=1;i<=Q;++i) printf("%lld\n",(ans[i].res[1]-ans[i].res[0])%mod); return 0;}
阅读全文
0 0
- 【BZOJ3626】LCA(树链剖分)
- BZOJ3626 [LNOI2014]LCA(树链剖分)
- bzoj3626 LCA 树链剖分
- [BZOJ3626][LNOI2014]LCA 树链剖分
- bzoj3626[LNOI2014]LCA 树链剖分
- [bzoj3626][LNOI2014]LCA 树链剖分
- [BZOJ3626][LNOI2014]LCA(离线+差分+树链剖分)
- BZOJ3626 LCA
- BZOJ3626 LCA
- 【Bzoj3626】LCA
- BZOJ3626 LCA
- bzoj3626: [LNOI2014]LCA (树链剖分+离线线段树)
- [BZOJ3626] LCA - 离线 - 树链剖分/动态树/分块
- [BZOJ3626][LNOI2014]LCA(离线+链剖)
- 【BZOJ3626】 [LNOI2014]LCA
- BZOJ3626: [LNOI2014]LCA
- bzoj3626【LNOI2014】LCA
- [BZOJ3626] [LNOI2014]LCA
- I/O管理器及IRP缓冲区管理
- Sprite.Create
- 【hdu 3785】寻找大富翁
- EXCEL之隐形对象的清理办法
- Linux常用命令-网络通信命令
- 【BZOJ3626】LCA(树链剖分)
- Hello Blog
- 数据库封装
- Down test
- 如何使用非模板类传递模板对象?
- 线程与进程
- mybatis笔记-3-事务
- django学习之路(二)
- centos6.5升级glibc