扫描线,从左到右依次处理每棵树。

用set按时间顺序维护影响了这棵树的所有操作,那么一个点的父亲就是它前面第一个操作1。

用Splay维护树的括号序列,那么两点间的距离就是括号数量减去匹配的括号个数。

添加或删除操作0就是单点换父亲,添加或删除操作1就是区间换父亲。可以通过添加虚点来实现区间换父亲操作。

时间复杂度$O(m\log m)$。

#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
const int N=200010;
int n,m,i,j,op,x,y,z,cnt,L[N],R[N],q[N+5][2],ans[N],tot,ap[N];
int ga0[N],gd0[N],ga1[N],gd1[N],v[N<<1],nxt[N<<1],ed,G[N],V[N],W[N],NXT[N],cq;
set<int>T0,T1;
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline void add(int&x,int y){v[++ed]=y;nxt[ed]=x;x=ed;}
inline void addq(int x,int y,int z){V[++cq]=y;W[cq]=z;NXT[cq]=G[x];G[x]=cq;}
namespace DS{
const int M=N<<2;
int son[M][2],f[M],size[M];
struct P{int x,y;P(){x=y=0;}P(int _x,int _y){x=_x,y=_y;}inline P operator+(const P&v){return y>v.x?P(x,y-v.x+v.y):P(x+v.x-y,v.y);}inline void operator+=(const P&v){*this=*this+v;}
}v[M],s[M];
inline void up(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1;s[x]=s[son[x][0]]+v[x]+s[son[x][1]];
}
inline void rotate(int x){int y=f[x],w=son[y][1]==x;son[y][w]=son[x][w^1];if(son[x][w^1])f[son[x][w^1]]=y;if(f[y]){int z=f[y];if(son[z][0]==y)son[z][0]=x;if(son[z][1]==y)son[z][1]=x;}f[x]=f[y];son[x][w^1]=y;f[y]=x;up(y);
}
inline void splay(int x,int w){while(f[x]!=w){int y=f[x];if(f[y]!=w){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}rotate(x);}up(x);
}
void spilt(int x,int y){splay(x,0);splay(y,x);f[son[x][0]]=f[son[y][1]]=0;if(son[x][0]&&son[y][1]){int z=son[x][0];while(son[z][1])z=son[z][1];splay(z,0);son[z][1]=son[y][1];f[son[y][1]]=z;up(z);}son[x][0]=son[y][1]=0;up(y);up(x);
}
inline int rank(int x){splay(x,0);return size[son[x][0]];
}
inline int ask(int x,int y){splay(x,0);splay(y,x);P t=s[son[y][0]]+v[y];return t.x+t.y;
}
inline void addright(int x,int y){splay(x,0);x=son[x][1];while(son[x][0])x=son[x][0];son[x][0]=y;f[y]=x;up(x);splay(y,0);
}
inline void newnode(int o,int val,int z){int x=o<<1,y=x|1;v[x]=P(0,val),v[y]=P(val,0);f[y]=x,son[x][1]=y;up(y);up(x);if(z)addright(z<<1,x);
}
}
inline void add0(int x){int y=q[x][1];set<int>::iterator j=T0.lower_bound(x);j--;DS::spilt(y<<1,y<<1|1);if(q[*j][0])DS::addright(q[*j][1]<<1,y<<1);else DS::addright(q[*j][1]<<1|1,y<<1);T0.insert(x);
}
inline void del0(int x){int y=q[x][1];T0.erase(x);DS::spilt(y<<1,y<<1|1);
}
inline void add1(int x){int y=q[x][1];set<int>::iterator j=T0.lower_bound(x),k;if(*j<N&&!q[*j][0]){k=T1.lower_bound(x);k=T0.find(*k);k--;DS::spilt(q[*j][1]<<1,q[*k][1]<<1|1);DS::addright(y<<1,q[*j][1]<<1);}T0.insert(x);T1.insert(x);
}
inline void del1(int x){int y=q[x][1],pos;T0.erase(x);T1.erase(x);set<int>::iterator j=T0.lower_bound(x),k,o;if(*j<N&&!q[*j][0]){k=T1.lower_bound(x);k=T0.find(*k);k--;o=T0.lower_bound(x);o--;DS::spilt(q[*j][1]<<1,q[*k][1]<<1|1);if(q[*o][0])DS::addright(q[*o][1]<<1,q[*j][1]<<1);else DS::addright(q[*o][1]<<1|1,q[*j][1]<<1);}
}
inline int query(int x,int y){if(x==y)return 0;if(DS::rank(x<<1)>DS::rank(y<<1))swap(x,y);return DS::ask(x<<1,y<<1);
}
int main(){read(n),read(m);L[cnt=1]=1,R[1]=n;q[0][0]=q[0][1]=1;for(i=1;i<=m+1;i++){ap[i]=i;DS::newnode(i,1,0);}tot=m+1;for(i=1;i<=m;i++){read(op),read(x),read(y);if(!op){L[++cnt]=x;R[cnt]=y;q[i][1]=cnt;add(ga0[x],i),add(gd0[y],i);}if(op==1){read(z);x=max(x,L[z]),y=min(y,R[z]);if(x>y)continue;DS::newnode(++tot,0,ap[z]);ap[z]=tot;q[i][0]=1,q[i][1]=tot;add(ga1[x],i),add(gd1[y],i);}if(op==2)read(z),addq(x,y,z);}T0.insert(0),T0.insert(N);T1.insert(0),T1.insert(N);for(i=1;i<=n;i++){for(j=ga0[i];j;j=nxt[j])add0(v[j]);for(j=ga1[i];j;j=nxt[j])add1(v[j]);for(j=G[i];j;j=NXT[j])ans[j]=query(V[j],W[j]);for(j=gd0[i];j;j=nxt[j])del0(v[j]);for(j=gd1[i];j;j=nxt[j])del1(v[j]);}for(i=1;i<=cq;i++)printf("%d\n",ans[i]);return 0;
}

  

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

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

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

  2. [2019.3.20]BZOJ4573 [Zjoi2016]大森林

    发现一个重要的性质:由于不存在删点的操作,所以之后的加点并不会对之前的询问的答案造成影响. 所以我们可以先处理修改,再处理询问. 考虑从左到右扫描每一颗树,每次将前一棵树通过一些修改变为后一棵树. 发 ...

  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. 3825. 逃离大森林

    3825. 逃离大森林 题目链接 #include <bits/stdc++.h> #define x first #define y second using namespace std ...

  9. CF_329_B----AcWing_3825_逃离大森林(BFS究极模板)

    原题链接:https://www.acwing.com/problem/content/3828/ 你是一个宝可梦饲养员,你正在进行你的冒险之旅. 当前,你的目标是逃离飞鸟森林. 飞鸟森林可以表示为一 ...

最新文章

  1. 如何在JavaScript中切片和拼接数组
  2. 中青评论:家政本科招生难,专业名字误终身?
  3. PADS LAYOUT的一般流程
  4. mysql 多级分类_数据库多级分类相关行排列在一起的查询
  5. HTML+CSS+JS实现 ❤️3D万花筒图片相册展示特效❤️
  6. 了解C#常用关键字 1214
  7. 提高linux运行速度,提高Linux操作系统的运行速度
  8. Android利用百度地图定位
  9. php之防注入程序绕过浅谈
  10. axios 全攻略之基本介绍与使用(GET 与 POST)
  11. 利用react native创建一个天气APP
  12. 一张火车票背后的生意【下篇】
  13. 数据分析实际案例之:pandas在泰坦尼特号乘客数据中的使用
  14. Unity引擎中的C#语言学习的笔记(1)
  15. 使用docker安装ubuntu镜像
  16. 解决usb otg线系统重启的问题
  17. [转载]【实拍】:绿色灾难中的我们|浒苔带来的启示
  18. LTspice raw文件格式
  19. 100G波分复用(WDM)宽带传输设备
  20. 怎样才能找回电脑上误删的文件呢?

热门文章

  1. iPhone开发笔记(1)MPMoviePlayerController的用法和播放时只有声音没有图像的解决办法...
  2. 关于IIS正在使用突然断电后,IIS不能使用解决方案
  3. Android国际化(多语言)实现,支持8.0
  4. 第 5 章 Spring Boot
  5. 20.27分发系统介绍;20.28expect脚本远程登录;20.29expect脚本远程执行命令;20.30expect脚本传递参数...
  6. HTMLCanvasElement.toDataURL()
  7. 使用图片方式自定义iOS导航栏navigationItem的backBarButtonItem
  8. vm虚拟机linux磁盘空间不足,手动扩大
  9. 通过组策略禁用U盘执行病毒文件
  10. ***网站必备:(经典语句)