发现一个重要的性质:由于不存在删点的操作,所以之后的加点并不会对之前的询问的答案造成影响。

所以我们可以先处理修改,再处理询问。

考虑从左到右扫描每一颗树,每次将前一棵树通过一些修改变为后一棵树。

发现我们修改一段区间的生长点的位置,相当于在扫描到这个区间的左端点时,将接在原来生长点下面的子树全部转移到新的生长点下面,在扫描右端点结束后,将新的生长点下面的子树移回去。

如下图(红色为原来生长点,蓝色为修改后生长点)

->

但是这样不方便对子树进行转移,我们想到可以将一棵子树的转移变为一个点的转移,然后用LCT维护。

于是我们在每次的生长点的孩子位置建一个虚点,将所有从生长点生长出来的点接在虚点的孩子上。

这样一来,我们转移子树的时候,只需要切断虚点和它的父亲的边,再把它连到新的生长点上就好了。

如下图

->

我们用\((1,p,x,y)\)表示在第\(p\)棵树上,将虚点\(x\)移动到点\(y\)的孩子上的操作。

然后,我们对于每一个1操作\(1\ l\ r\ y\),将它拆成两个操作:

\((1,l,x,y)\)将一个虚点\(x\)在左端点\(l\)的树上移动到\(y\)的孩子上(关于虚点\(x\)从哪里来之后会讲);

\((1,r+1,x,li)\)将一个虚点\(x\)在右端点后一个点\(r+1\)的树上移动到\(x\)被建立时的上一个虚点\(li\)的孩子上(之后会讲),即撤销之前在左端点上的移动。

然后考虑具体实现

一开始,我们维护第0棵树,即所有的节点都生长在1号节点的孩子上。

初始有两个节点:实点1和虚点2,2是1的孩子。

对于一个0操作,我们新建一个节点,同时维护一个从实点集合到节点集合的映射\(mp\),\(mp_i\)表示第\(i\)个实点是第几个节点,将它连接在目前上一个被建立的虚点上,并同时记录存在第\(i\)个实点的树的区间\([sgl_i,sgr_i]\)。

对于一个1操作,我们新建一个虚点\(x\),执行上述操作。

对于一个2操作,我们用\((2,p,x,y)\)表示询问第\(p\)棵树上,节点(注意不是实点)\(x\)和\(y\)的距离。

注意,此时我们并没有执行由\(1\)操作和\(2\)操作获得的操作和询问。

然后,我们要对所有的操作排序,第一关键字为操作或者询问的位置(位置靠前的排在前面),第二关键字为询问或操作(询问排在操作前面)

然后对于一个操作,我们断开需要移动的点和它原来的父亲,然后将它连在新父亲的孩子上。

对于一个询问,我们运用树上差分,给实点赋权为1,虚点赋权为0(这一步在建立节点时就可以实现),记录\(sum_x\)表示节点\(x\)到1路径上节点的权值和,那么节点\(i,j\)之间的路径长度=\(sum_i+sum_j-sum_{LCA(i,j)}\)。

最后一个问题,如何在LCT上求LCA?

显然我们不能\(Split\)(即进行\(Makeroot\)然后\(Access\)),因为这是一棵有根(1)树,这样会改变它的结构。

于是我们对于节点\(i,j\)分别进行\(Access\),后一次中最后一个与右孩子切断实边的节点即为LCA。

为什么?其实最后一次切断目前节点与右孩子的实边后,目前节点到根的部分即为两点到根路径的公共部分,自然这个点就是\(i\)和\(j\)的LCA。

code:

#include<bits/stdc++.h>
#define ci const int&
using namespace std;
struct query{int op,pos,x,y,id;//move x to son of y(op=1),query the lenth of (x,y)(op=2)
}q[400010];
struct node{int f,c[2],tag,val,sum;
}t[200010];
int n,m,sz,tt,T,len,op,a,b,c,cnt,li,mp[200010],rn,sgl[200010],sgr[200010],prt[200010];
bool cmp(query x,query y){return x.pos!=y.pos?x.pos<y.pos:x.op<y.op;
}
void Upd(ci x){t[x].sum=t[t[x].c[0]].sum+t[t[x].c[1]].sum+t[x].val;
}
void Psd(ci x){t[x].tag?swap(t[x].c[0],t[x].c[1]),t[t[x].c[0]].tag^=1,t[t[x].c[1]].tag^=1,t[x].tag=0:0;
}
bool Isroot(ci x){return t[t[x].f].c[0]!=x&&t[t[x].f].c[1]!=x;
}
void Rotate(ci x){int y=t[x].f,z=t[y].f;Psd(y),Psd(x);int c=t[y].c[1]==x,gc=t[z].c[1]==y;!Isroot(y)?t[z].c[gc]=x:0,t[x].f=z;t[y].c[c]=t[x].c[c^1],t[t[x].c[c^1]].f=y;t[x].c[c^1]=y,t[y].f=x;Upd(y),Upd(x);
}
void Splay(ci x){while(!Isroot(x)){int y=t[x].f,z=t[y].f;if(Isroot(y))Rotate(x);else{Psd(z),Psd(y);(t[y].c[1]==x)==(t[z].c[1]==y)?Rotate(y):Rotate(x),Rotate(x);}}
}
int Access(ci x,ci lst){if(!x)return 0;Splay(x),t[x].c[1]=lst,Upd(x);int tmp=Access(t[x].f,x);return tmp?tmp:x;
}
void Cut(ci x){Access(x,0),Splay(x),t[x].c[0]=t[t[x].c[0]].f=0,Upd(x);
}
void Link(ci x,ci y){Splay(y),t[y].f=x;
}
void printpath(int x){if(!x)return;Splay(x);t[x].c[0]?printpath(t[x].c[0]):printpath(t[x].f);
}
int main(){scanf("%d%d",&n,&m);rn=1,li=cnt=2,t[1].val=t[1].sum=1,Link(1,2),mp[1]=sgl[1]=1,sgr[1]=n;while(m--){scanf("%d",&op);if(op==0)scanf("%d%d",&a,&b),mp[++rn]=++cnt,sgl[rn]=a,sgr[rn]=b,Link(li,cnt),t[cnt].val=t[cnt].sum=1;else if(op==1){scanf("%d%d%d",&a,&b,&c),a=max(a,sgl[c]),b=min(b,sgr[c]);if(a>b)continue;Link(li,++cnt),q[++sz]=(query){1,a,cnt,mp[c],0},q[++sz]=(query){1,b+1,cnt,li,0},li=cnt;}else ++sz,scanf("%d%d%d",&q[sz].pos,&q[sz].x,&q[sz].y),q[sz].x=mp[q[sz].x],q[sz].y=mp[q[sz].y],q[sz].op=2,q[sz].id=++tt;}sort(q+1,q+sz+1,cmp);for(int i=1;i<=sz;++i){if(q[i].op==1)Cut(q[i].x),Link(q[i].y,q[i].x);else Access(q[i].x,0),Splay(q[i].x),len=0,len+=t[q[i].x].sum,T=Access(q[i].y,0),Splay(q[i].y),len+=t[q[i].y].sum,Access(T,0),prt[q[i].id]=len-(t[T].sum<<1);}for(int i=1;i<=tt;++i)printf("%d\n",prt[i]);return 0;
}

转载于:https://www.cnblogs.com/xryjr233/p/BZOJ4573.html

[2019.3.20]BZOJ4573 [Zjoi2016]大森林相关推荐

  1. [BZOJ4573][[Zjoi2016]大森林][LCT建虚点]

    [BZOJ4573][[Zjoi2016]大森林][LCT建虚点] 题意懒得写了.... 思路: 建LCT的时候我们可以引入虚点.对于所有的1操作,新建一个没有权值的虚点,然后对于0操作,可以把新建的 ...

  2. BZOJ4573 : [Zjoi2016]大森林

    扫描线,从左到右依次处理每棵树. 用set按时间顺序维护影响了这棵树的所有操作,那么一个点的父亲就是它前面第一个操作1. 用Splay维护树的括号序列,那么两点间的距离就是括号数量减去匹配的括号个数. ...

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

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

  4. bzoj 4573: [Zjoi2016]大森林

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

  5. [ZJOI2016]大森林

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

  6. [ZJOI2016]大森林(LCT)

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

  7. 「ZJOI2016」大森林 解题报告

    「ZJOI2016」大森林 神仙题... 很显然线段树搞不了 考虑离线操作 我们只搞一颗树,从位置1一直往后移动,然后维护它的形态试试 显然操作0,1都可以拆成差分的形式,就是加入和删除 因为保证了操 ...

  8. 【最新】2019年最新青甘大环线攻略收藏版!

    青海分享 首页 快讯 资讯 习俗 分享 大美青海 更多 青海分享 我要投稿 首页精彩分享 [最新]2019年最新青甘大环线攻略收藏版! 青海分享 • 2019-09-06 13:12 • 精彩分享 • ...

  9. kaggle经典题--“泰坦尼克号”--0.8275准确率--东北大学20级python大作业开源(附详细解法与全部代码以及实验报告)

    kaggle经典题--"泰坦尼克号"--0.8275准确率--东北大学20级python大作业开源(附详细解法与全部代码以及实验报告) 前言 开发环境 一.导入包: 二.实验数据的 ...

最新文章

  1. 有了这款可视化工具,Java 应用性能分析、调优 so easy...
  2. AD18 KeepOut不能打孔,转成3D不显示孔的位置
  3. 有符号数、无符号树混合计算问题。
  4. 《CCNA学习指南:数据中心(640-911)》——1.6 考试要点
  5. 剑指offer (01):赋值运算符函数 (C++ 实现)
  6. r语言中的while循环_R编程中的While循环
  7. rpc服务器进不了系统,Win7系统RPC服务器不可用怎么解决?
  8. 先有本地代码,后有远程仓库
  9. 知识库的构建 3-1 被命名的实体识别分类 NERC
  10. Android转场动画深度解析(3)
  11. WCF 4.0路由服务Routing Service
  12. 用Java实现并查集
  13. 全国31个省份农产品进口出口额省级数据2001-2021
  14. 【Python】创蓝253云通讯平台---国际短信API接口demo
  15. mysql 异常码1903_Mysql 异常。 寻求帮助
  16. 什么耳机适合跑步、分享五款公认最好的跑步耳机
  17. 读《游戏之旅-我的编程感悟》笔记
  18. Android kotlin使用id直接做view的引用
  19. IBM开放创新推动和谐区域医疗
  20. [生命科学] 生物基础实验之PCR验证

热门文章

  1. 怎么使用cmd命令更改文件后缀
  2. matlab 匿名函数
  3. 修改计算机网络适配器的IPv4地址
  4. c语言作业i love gplt,集思广益 | 寒假天梯赛准备第一阶段总结
  5. 苹果定时开关机怎么设置_小编详解电脑怎么定时开关机
  6. 这台 ThinkPad 的指纹识别器为什么在电源按钮上?
  7. 操作系统 读者写者问题的实现(C++ 读者优先、写者优先)
  8. 联想旭日410M笔记本,在Windows Server 2003系统下的声卡驱动安装问题
  9. android 打印机怎么拿到蓝牙地址,Android 商米蓝牙打印机的使用方式
  10. Vivado官网获取License