题链:

https://www.luogu.org/problemnew/show/P3348

题解:

LCT,神题
首先有这么一个结论:
每次的1操作(改变生长点操作),一定只会会对连续的一段区间产生影响。
(即不存在对两段不相连的区间都进行了该操作的情况,令这种情况为[2])
简单来说就是因为该操作需要对应的那些树存在那个节点。
如果发生了情况[2],即表明区间[l1,r1],[l2,r2]有节点x,且[l1+1,r2-1]无节点x,且r2-1>=l1+1
但是我们的生长操作0操作每次进行的区间都是连续的,且每次产生的节点的编号都不同,
所以不会存在"区间[l1,r1],[l2,r2]有节点x,且[l1+1,r2-1]无节点x,且r2-1>=l1+1"这种情况
也就不会有情况[2]了。

所以,我们可以记录下来每种节点x的在哪段区间里L[x],R[x]。
然后对于一个1操作(1,l,r,x),我们就可以得到其真正会影响的那段连续区间[max(l,L[x]),min(r,R[x])]
再强调一下,对于计算出来的区间[max(l,L[x]),min(r,R[x])],该操作是一定会影响到的,同时也只会影响到该区间。

然后再看看由于询问保证一定给出的点存在,
所以既然我们都已经得到了1操作会真正影响的区间,那么对于所有的0操作(生长操作),
我们就可以忽略掉其给出的区间限制,而让整个[1,N]的区间都执行这个操作。
这样并不会影响答案。
(因为询问不会问到这个点,1操作也因为已经算出了其确切会影响的区间)。

由于树很多,不能同时维护这么多颗树,我们考虑离线,并用LCT维护
把所有操作和询问按其影响的区间的端点挂在对应的位置上,
依次维护每一颗树,并回答关于该树的问题。
同样由于询问保证一定给出的点存在,所以我们把询问放在最后来做。(即把当前树的形态维护好后,再回答所有询问)
然后是如何维护树的形态,
1.对于一个每一个1操作(1,l,r,x),我们都新建一个虚节点,权值为0,
初始时该点先连在上一个虚节点上,之后所有的点都连在这个新的虚节点上面。
然后当到了第[max(l,L[x])颗树时,就让它连着它下面那一包东西,
先和它的父亲断开,再整体连到x对应的节点上去,实现了把之后的节点长在x上操作。
然后当到了第min(r,R[x])]+1颗树时,就让该虚点又连着它下面的那一包东西,回到之前的位置上去,实现了该操作没有影响的区间,生长节点不动这个操作。
2.对于一个0操作,因为我们让它对全区间生效,
所以只要遇到0操作,就把该操作对应的权值为1的节点link到上一个虚节点,以便之后随着虚节点被一起打包着走。
3.对于2操作,现在需要询问了,由于该树的形态已经维护好,所以直接找到两个点lca并回答即可。

(思路太妙啊,我都不知道下次遇到类似的题目能不能想出这种解法。2333)

代码:

  1. #include<bits/stdc++.h>
  2. #define MAXN 400005
  3. using namespace std;
  4. struct LCT{
  5. int lnt;
  6. int ch[MAXN][2],size[MAXN],val[MAXN],fa[MAXN];
  7. LCT(){lnt=1;}
  8. bool Who(int x){return ch[fa[x]][0]!=x;}
  9. bool Isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
  10. int Newnode(int v){
  11. fa[lnt]=ch[lnt][0]=ch[lnt][1]=0;
  12. size[lnt]=val[lnt]=v;
  13. return lnt++;
  14. }
  15. void Pushup(int x){
  16. size[x]=val[x];
  17. if(ch[x][0]) size[x]+=size[ch[x][0]];
  18. if(ch[x][1]) size[x]+=size[ch[x][1]];
  19. }
  20. void Rotate(int x){
  21. static int y,z,l1,l2;
  22. y=fa[x]; z=fa[y];
  23. l1=Who(x); l2=Who(y); fa[x]=z;
  24. if(!Isroot(y)) ch[z][l2]=x;
  25. fa[ch[x][l1^1]]=y; fa[y]=x;
  26. ch[y][l1]=ch[x][l1^1]; ch[x][l1^1]=y;
  27. Pushup(y);
  28. }
  29. void Splay(int x){
  30. static int y;
  31. for(;y=fa[x],!Isroot(x);Rotate(x))
  32. if(!Isroot(y)) Rotate(Who(y)==Who(x)?y:x);
  33. Pushup(x);
  34. }
  35. int Access(int x){
  36. static int y;
  37. for(y=0;x;y=x,x=fa[x])
  38. Splay(x),ch[x][1]=y,Pushup(x);
  39. return y;
  40. }
  41. int Findlca(int x,int y){
  42. Access(x); return Access(y);
  43. }
  44. void Link(int x,int y){
  45. //Splay(y); It's not neccessary to do the Splay operation.
  46. fa[x]=y;
  47. }
  48. void Cut(int x){
  49. Access(x); Splay(x);
  50. fa[ch[x][0]]=0; ch[x][0]=0; Pushup(x);
  51. }
  52. }DT;
  53. struct Cmd{
  54. int pos,odr,from,to;
  55. Cmd(){}
  56. Cmd(int _a,int _b,int _c,int _d):pos(_a),odr(_b),from(_c),to(_d){}
  57. bool operator < (const Cmd &rtm) const{
  58. return pos<rtm.pos||(pos==rtm.pos&&odr<rtm.odr);
  59. }
  60. }Q[MAXN];
  61. int id[MAXN],L[MAXN],R[MAXN],ANS[MAXN];
  62. int N,M,unreal,dnt=1,cnt=1;
  63. int main(){
  64. ios::sync_with_stdio(0);
  65. cin>>N>>M;
  66. L[dnt]=1; R[dnt]=N;
  67. id[dnt++]=DT.Newnode(1);
  68. unreal=DT.Newnode(0);
  69. DT.Link(unreal,id[1]);
  70. for(int i=1,t,l,r,x,tmp;i<=M;i++){
  71. cin>>t;
  72. switch(t){
  73. case 0:
  74. cin>>l>>r;
  75. L[dnt]=l; R[dnt]=r;
  76. id[dnt]=DT.Newnode(1);
  77.  
  78. Q[cnt++]=Cmd(1,i-M,id[dnt],unreal);
  79. dnt++;
  80. //Can the real node be linked to unreal node at now instead of doing this later?
  81. //DT.Link(id[dnt],unreal);
  82. //After the try,I found it okay to do this.
  83. break;
  84. case 1:
  85. cin>>l>>r>>x;
  86. l=max(l,L[x]); r=min(r,R[x]);
  87. if(l<=r){
  88. tmp=DT.Newnode(0);
  89. DT.Link(tmp,unreal);
  90. Q[cnt++]=Cmd(l,i-M,tmp,id[x]);
  91. Q[cnt++]=Cmd(r+1,i-M,tmp,unreal);
  92. unreal=tmp;
  93. }
  94. break;
  95. case 2:
  96. cin>>x>>l>>r;
  97. Q[cnt++]=Cmd(x,i,id[l],id[r]);
  98. break;
  99. }
  100. }
  101. sort(Q+1,Q+cnt);
  102. memset(ANS,-1,sizeof(ANS));
  103. for(int tree=1,i=1,x,y;i<cnt&&tree<=N;tree++){
  104. while(Q[i].pos==tree){
  105. x=Q[i].from; y=Q[i].to;
  106. if(Q[i].odr>0){
  107. int l1,l2,l3,lca;
  108. DT.Access(x); DT.Splay(x); l1=DT.size[x];
  109. lca=DT.Findlca(x,y); DT.Access(lca); DT.Splay(lca); l2=DT.size[lca];
  110. DT.Access(y); DT.Splay(y); l3=DT.size[y];
  111. ANS[Q[i].odr]=l1+l3-2*l2;
  112. }
  113. else DT.Cut(x),DT.Link(x,y);
  114. i++;
  115. }
  116. }
  117. for(int i=1;i<=M;i++) if(ANS[i]>=0) cout<<ANS[i]<<endl;
  118. return 0;
  119. }

  

●洛谷P3348 [ZJOI2016]大森林的更多相关文章

  1. 洛谷P3348 [ZJOI2016]大森林(LCT,虚点,树上差分)

    洛谷题目传送门 思路分析 最简单粗暴的想法,肯定是大力LCT,每个树都来一遍link之类的操作啦(T飞就不说了) 考虑如何优化算法.如果没有1操作,肯定每个树都长一样.有了1操作,就来仔细分析一下对不 ...

  2. 洛谷P3348 [ZJOI2016]大森林 [LCT]

    传送门 刷了那么久水题之后终于有一题可以来写写博客了. 但是这题太神仙了我还没完全弄懂-- upd:写完博客之后似乎懂了. 思路 首先很容易想到\(O(n^2\log n)\)乘上\(O(\frac{ ...

  3. P3348 [ZJOI2016]大森林

    \(\color{#0066ff}{ 题目描述 }\) 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点, ...

  4. P3348 [ZJOI2016]大森林(Link-cut-tree)

    传送门 题解 题面大意: \(0.\)区间加节点 \(1.\)区间换根 \(2.\)单点询问距离 如果没有\(1\)操作,因为区间加节点都是加在下面,所以我们可以直接把\(n\)棵树压成一棵树,直接询 ...

  5. P3348 [ZJOI2016]大森林(LCT)

    Luogu3348 BZOJ4573 LOJ2092 题解 对于每个\(1\)操作建一个虚点,以后的\(0\)操作都连在最近建好的虚点上.这样每次整体嫁接的时候,直接把这个虚点断掉它原来的父亲,再\( ...

  6. 洛谷 P1230 智力大冲浪

    洛谷 P1230 智力大冲浪 题目描述 小伟报名参加中央电视台的智力大冲浪节目.本次挑战赛吸引了众多参赛者,主持人为了表彰大家的勇气,先奖励每个参赛者m元.先不要太高兴!因为这些钱还不一定都是你的?! ...

  7. BZOJ4573:[ZJOI2016]大森林——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=4573 https://www.luogu.org/problemnew/show/P3348#sub ...

  8. [ZJOI2016]大森林(LCT)

    题目描述 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力. 小Y掌握了一种 ...

  9. [ZJOI2016]大森林

    Description: 小Y家里有一个大森林,里面有n棵树,编号从1到n 0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 号操作叶子标号加 1(例 ...

随机推荐

  1. Alpha冲刺No.10

    一.站立式会议 我们的阿尔法冲刺也基本宣告血崩,虽然很多功能已经实现,但是并没有串联在一起,好在这周不需要上课,我们也能好好睡一觉 实现手机的定位系统 细化界面设计 解决数据库和软件交互的一些问题 二 ...

  2. 课后练习:C语言实现Linux命令——od

    课后练习:C语言实现Linux命令--od --------CONTENTS-------- 题目详情与分析 设计思路 遇到的问题及解决 待实现的设想与思考 学习反思与感悟 附1:myod.c「1.0 ...

  3. 下载文件downloadFile

    public static void downLoadFile(InputStream inStream, String fileName) { if (StringUtils.isBlank(fil ...

  4. 初谈Git(本机克隆项目远程仓库)

    1. 码云注册与新建项目 注册并新建项目 2. Git安装并配置 安装 配置 3. clone项目 附:一些Git命令 git clone 拷贝并跟踪远程的master分支 git add 跟踪新文件 ...

  5. 201621123043《java程序设计》第4周学习总结

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 关键字:继承.覆盖.多态 1.2 尝试使用思维导图将这些关键词组织起来.注:思维导图一般不需要出现过多的字. 1.3 可选:使用 ...

  6. Gson解析Json数组

    需求:从steam官网获取英雄数据,即为Json数据,并导入到本地数据库 Json数据是这样的 { "result": { "heroes": [ { &quo ...

  7. python基础学习篇章一

    一. 对Python的认识 1. Python的标准实现方式是将源代码的语句编译为字节码的形式,之后再将字节码解释出来.由于字节码是一种与平台无关的形式,字节码具有可移植性.但是Python没有将代码 ...

  8. php面向对象相关内容

    1.什么是面向对象? 面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程架构,OOP的一条基本原则是计算机程序是由单个能够起到子程序作 ...

  9. WIN7 局域网共享打印机每次电脑重启后必须登录密码重新连接问题修复

    第一步,WIN+R(或者开始->附件->运行)输入gpedit或gpedit.msc 进入 第二步:把这几个拒绝的Guest给删除掉,也可以只删除""拒绝从王洛访问这台 ...

  10. Win10下, TortoiseGit安装及配合Gitee使用完整版

    Windows10下, TortoiseGit的安装及使用, 并配合Gitee码云使用! 1) 安装TortoiseGit 官网, 32位, 64位, 自选 https://tortoisegit.o ...