P3348-[ZJOI2016]大森林【LCT】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P3348


1|1题目大意

n棵树开始只有一个编号为1的节点且为标记点。m次操作要求支持

  1. lr的树中的标记点下面加入一个新的编号的节点
  2. lr的树上的标记点改为x(如果没有节点x就不操作)
  3. 询问第x棵树上u点到v点的距离

1n105,1m2×105
保证询问合法


1|2解题思路

保证询问合法的话我们其实第一个操作理解为对所有树都操作就可以了。主要是第二个操作,在线区间LCT看起来就很不可做,所以考虑离线。

对于一个操作1 l r x它会对l1l的树造成的影响是再往后直到下一个1操作之间所有的节点都会被接到不同的点下面。但是显然暴力改接是不行的,我们可以考虑对于两个1操作之间的0操作建立一个虚点下面链接的所有这个区间新建的点,然后每次就改接一个虚点就好了。

然后需要注意的一些细节:因为根是固定的不能用split,会破坏父子关系(好像在makeroot(1)回去可以,但是据说很慢?),所以要差分求到根节点的路径长度。还要求lcaLCT上求lca的话就accessx再到y最后Splay的那个y就是lca了。

还有因为如果没有节点x就不操作,所以我们需要记录一下每个点拥有的树的区间然后取一个交集就好了。

时间复杂度O(nlogn)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=2e5+10; struct node{ int x,l,r,id; }q[N],c[N]; int n,m,num,cnt,ct,qt; int L[N],R[N],ans[N],at[N]; struct LCT{ int fa[N],t[N][2],siz[N],v[N]; bool Nroot(int x) {return fa[x]&&(t[fa[x]][0]==x||t[fa[x]][1]==x);} bool Direct(int x) {return t[fa[x]][1]==x;} void PushUp(int x){ siz[x]=siz[t[x][0]]+siz[t[x][1]]+v[x]; return; } void Rotate(int x){ int y=fa[x],z=fa[y]; int xs=Direct(x),ys=Direct(y); int w=t[x][xs^1]; if(Nroot(y))t[z][ys]=x; t[x][xs^1]=y;t[y][xs]=w; if(w)fa[w]=y;fa[y]=x;fa[x]=z; PushUp(y);PushUp(x);return; } void Splay(int x){ while(Nroot(x)){ int y=fa[x]; if(!Nroot(y))Rotate(x); else if(Direct(x)==Direct(y)) Rotate(y),Rotate(x); else Rotate(x),Rotate(x); } return; } int Access(int x){ int y=0,px=x; for(;x;y=x,x=fa[x]) Splay(x),t[x][1]=y,PushUp(x); Splay(px);return y; } void Link(int x,int y) {Splay(x);fa[x]=y;return;} void Cut(int x) {Access(x);fa[t[x][0]]=0;t[x][0]=0;PushUp(x);return;} }T; bool cmp(node x,node y) {return x.x<y.x;} int main() { scanf("%d%d",&n,&m); L[1]=cnt=at[1]=1;R[1]=n; T.Link(2,1);cnt=2;int last=2,num=1,aux=2; for(int i=1;i<=m;i++){ int op,l,r,x; scanf("%d%d%d",&op,&l,&r); if(op==0){ ++num;at[num]=++cnt; T.v[cnt]=T.siz[cnt]=1; T.Link(cnt,aux); L[num]=l;R[num]=r; } else if(op==1){ scanf("%d",&x); l=max(l,L[x]);r=min(r,R[x]); if(l>r)continue; ++cnt;T.Link(cnt,aux); c[++ct]=(node){l,cnt,at[x]}; c[++ct]=(node){r+1,cnt,aux,0}; aux=cnt; } else{ scanf("%d",&x); q[++qt]=(node){l,at[r],at[x],qt}; } } sort(q+1,q+1+qt,cmp); sort(c+1,c+1+ct,cmp); for(int i=1,z=1;i<=qt;i++){ int sum=0; while(z<=ct&&c[z].x<=q[i].x) T.Cut(c[z].l),T.Link(c[z].l,c[z].r),z++; T.Access(q[i].l);sum+=T.siz[q[i].l]; int lca=T.Access(q[i].r);sum+=T.siz[q[i].r]; T.Access(lca);sum-=2*T.siz[lca]; ans[q[i].id]=sum; } for(int i=1;i<=qt;i++) printf("%d\n",ans[i]); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/14481919.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(44)  评论(0编辑  收藏  举报
编辑推荐:
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
阅读排行:
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· 语音处理 开源项目 EchoSharp
· 《HelloGitHub》第 106 期
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 使用 Dify + LLM 构建精确任务处理应用
点击右上角即可分享
微信分享提示