【LuoguP3348】[ZJOI2016]大森林
题目描述
小Y家里有一个大森林,里面有n棵树,编号从1到n。一开始这些树都只是树苗,只有一个节点,标号为1。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。
小Y掌握了一种魔法,能让第l棵树到第r棵树的生长节点长出一个子节点。同时她还能修改第l棵树到第r棵树的生长节点。她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?
Sol
一脸毒瘤。
区间 \(link\) 一个叶子 , 显然不能模拟吧...
这种复杂的题显然都是只寻求我们需要知道的信息。
我们只需要知道某棵树里两个点之间的距离就行了。
注意到题目中说了保证 \(u,v\) 是存在于要询问的树中的 , 容易发现我们先把树该加的点都加了最后再来询问也是木有关系的。
这样再想一下,每次加叶子的编号是一定的 !! 所以那个什么区间加叶子也是假的 , 我们并不关心我们是否加错了叶子 , 反正不影响询问。\((OVO)\)
所以我们仅仅需要一棵树。
但是一棵树显然不能同时处理不同树上的询问。
这时就容易发现这是一个二维空间里的问题了 , 以时间和位置为轴把操作写在二维平面上的话 , 就可以用一个扫描线来维护每一个位置上树的形态了并求解每一棵树的询问了。
唯一要考虑的就是换根了。
我们需要换根的话还不就是叶子接错地方了嘛 , 考虑什么情况下我们叶子接错了地方。
首先需要有一个换根操作 , 之后我们接了点叶子在原来的根上 , 就 wa 了。
所以我们要把它们搬运回去。也就是说 , 对于一个区间 \([L,R]\) 的换根操作。
当扫描线到了 \(L\) 的时候 , 我们需要把时间在此换根操作之后的点从原来的父亲上扯下来连到当前的新的根上 , 也就是后缀换根\(\dots\)然后出了这个区间后,也就是在 \(R+1\) 的位置 , 我们要把他们又全部插回去。
怎样高效地做到这种奇葩的操作。
我们对于每一个换根操作建立一个虚点 , 把生长操作以及换根操作的(虚)点都直接连到他们时间上的前驱虚点上。(特别的,没有前驱虚点的连在1号点上)
一开始 , 由于所有点都连向了虚点 , 那么如果删去虚点 , 这样就是一个菊花图。
之后 , 按照扫描线的顺序进行操作 , 改换根的就直接把这个虚点换个父亲根 , 要插回去就一样的移动虚点 , 这样不就后缀换根了吗!
要注意的细节地方就是求距离了 , 由于建立了虚点 , 不能直接算距离 , 但是还是可以用\(dis(u,v)=dis(u,root)+dis(v,root)-2*dis(LCA(u,v),root)\)来正确计算距离 , 每次计算前让1号点成为根就行了。
code:
#include<bits/stdc++.h>
using namespace std;
template<class T>inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;return;
}
#define ls son[0]
#define rs son[1]
#define __ NULL
#define get_son(a) (a->fa->rs==a)
#define get(a,b,c) (a? a->b:c)
#define IS(a) (a&&(!a->fa||(a->fa->son[get_son(a)]!=a)))
#define ID(a) ((a)-T)
int n,m;
const int N=4e5+10;
struct node{
node*son[2],*fa;bool rev;int size,cnt;
node* Tfa;
node(){ls=rs=fa=Tfa=__,size=rev=0;}
}T[N],*stk[N];int top=0,cnt=0;
node *aim[N];
inline void push_down(node*p){
if(!p||!p->rev) return;swap(p->ls,p->rs),p->rev=0;
if(p->ls) p->ls->rev^=1;if(p->rs) p->rs->rev^=1;return;
}
inline void Push(node*p){
stk[top=1]=p;while(!IS(p)) p=p->fa,stk[++top]=p;
while(top) push_down(stk[top--]);return;
}
inline void update(node*p){if(p)p->size=get(p->ls,size,0)+get(p->rs,size,0)+p->cnt;}
inline void rotate(node*p){
int k=get_son(p);node*q=p->fa,*gp=p->fa->fa;
q->son[k]=p->son[k^1];
if(p->son[k^1]) p->son[k^1]->fa=q;
if(!IS(q)) gp->son[get_son(q)]=p;
p->fa=gp,q->fa=p,p->son[k^1]=q;
return update(q);
}
inline void Splay(node*p){
if(!p)return;Push(p);
for(;!IS(p);rotate(p)) if(IS(p->fa))continue;else get_son(p->fa)==get_son(p)? rotate(p->fa):rotate(p);
return update(p);
}
inline node* access(node*p){node*lst=p;for(node*pre=__;p;pre=p,p=p->fa)Splay(p),lst=p,p->rs=pre,update(p);return lst;}
inline node* find(node*p){for(access(p),Splay(p);p->ls;p=p->ls);return p;}
inline void make_root(node*p){access(p),Splay(p),p->rev^=1;}
inline void split(node*p,node*q){make_root(p),access(q),Splay(q);}
inline void link(node*p,node*q){split(p,q);p->fa=q;}
inline void cut(node*p,node*q) {split(p,q);if(q->ls==p)p->fa=q->ls=__,update(q);}
struct Qry{
int op,pos,t,A,B;
inline bool operator <(Qry B)const{if(pos!=B.pos) return pos<B.pos;return t<B.t;}
}Q[N];
struct Que{
int id,pos,u,v;
inline bool operator <(Que B)const{return pos<B.pos;}
}que[N];int Qid=0;
int num0=0,num1=0,tot=0;
int ans[N],L[N],R[N];
inline int Query(node*p,node*q){
make_root(&T[1]);
access(p);Splay(p);
int dep1=get(p->ls,size,0)+p->cnt-1;
node*lca=access(q);Splay(q);
int dep2=get(q->ls,size,0)+q->cnt-1;
access(lca);Splay(lca);
int dep3=lca? (get(lca->ls,size,0)+lca->cnt-1):0;
int res=dep1+dep2-(dep3<<1);
return res;
}
int main()
{
init(n),init(m);
cnt=1;L[1]=1,R[1]=n;
T[1].size=T[1].cnt=1;T[1].Tfa=__;
for(int i=1;i<=m;++i) {
int op;init(op);
int x,u,v,l,r;
if(op==2) {
init(x);init(u),init(v);
++Qid;que[Qid]=(Que){Qid,x,u,v};
}
else if(op==0) {
++num0;init(l),init(r);
L[num0+1]=l;R[num0+1]=r;
Q[++tot]=(Qry){op,l,i,num1,r};
}
else {
init(l),init(r),init(x);
l=max(l,L[x]),r=min(r,R[x]);
if(l<=r) {
++tot;++num1;
Q[tot]=(Qry){1,l,i,num1,x};
++tot;
Q[tot]=(Qry){2,r+1,i,num1,num1-1};
}
}
}cnt=num0+1;aim[0]=&T[1];int h=0,preid=1;
for(int i=1;i<=cnt;++i) T[i].size=T[i].cnt=1;
for(int i=1;i<=tot;++i) {
if(Q[i].op==0){++preid;T[preid].fa=T[preid].Tfa=aim[h];}
else {if(Q[i].t==Q[i-1].t) continue;++h;aim[h]=&T[++cnt];T[cnt].fa=T[cnt].Tfa=aim[h-1];}
}
sort(que+1,que+1+Qid);sort(Q+1,Q+1+tot);h=1;
for(int i=1;i<=tot;++i) {
while(h<=Qid&&Q[i].pos>que[h].pos){ans[que[h].id]=Query(&T[que[h].u],&T[que[h].v]);++h;}
if(Q[i].op==0) continue;
else {
int u=Q[i].A,v=Q[i].B;
if(Q[i].op==1) {
cut(aim[u],aim[u]->Tfa);
aim[u]->Tfa=&T[v];
link(aim[u],&T[v]);
}else {
cut(aim[u],aim[u]->Tfa);
aim[u]->Tfa=aim[v];
link(aim[u],aim[v]);
}
}
}
while(h<=Qid) ans[que[h].id]=Query(&T[que[h].u],&T[que[h].v]),++h;
for(int i=1;i<=Qid;++i) printf("%d\n",ans[i]);
}
【LuoguP3348】[ZJOI2016]大森林的更多相关文章
- [ZJOI2016]大森林(LCT)
题目描述 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力. 小Y掌握了一种 ...
- [ZJOI2016]大森林
Description: 小Y家里有一个大森林,里面有n棵树,编号从1到n 0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 号操作叶子标号加 1(例 ...
- 【刷题】BZOJ 4573 [Zjoi2016]大森林
Description 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力.小 ...
- BZOJ4573:[ZJOI2016]大森林——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=4573 https://www.luogu.org/problemnew/show/P3348#sub ...
- bzoj 4573: [Zjoi2016]大森林
Description 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树 都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力. ...
- P3348 [ZJOI2016]大森林
\(\color{#0066ff}{ 题目描述 }\) 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点, ...
- 洛谷P3348 [ZJOI2016]大森林 [LCT]
传送门 刷了那么久水题之后终于有一题可以来写写博客了. 但是这题太神仙了我还没完全弄懂-- upd:写完博客之后似乎懂了. 思路 首先很容易想到\(O(n^2\log n)\)乘上\(O(\frac{ ...
- BZOJ4573 : [Zjoi2016]大森林
扫描线,从左到右依次处理每棵树. 用set按时间顺序维护影响了这棵树的所有操作,那么一个点的父亲就是它前面第一个操作1. 用Splay维护树的括号序列,那么两点间的距离就是括号数量减去匹配的括号个数. ...
- ●洛谷P3348 [ZJOI2016]大森林
题链: https://www.luogu.org/problemnew/show/P3348 题解: LCT,神题 首先有这么一个结论: 每次的1操作(改变生长点操作),一定只会会对连续的一段区间产 ...
随机推荐
- 怎么用jira写bug
工具/原料 有网的电脑 方法/步骤1: 打开公司给的访问JIRA的链接,输入公司给你注册的账号和密码,点击登录 方法/步骤2: 点击JIRA主菜单上的“创建”,进入编辑bug界面 方法/步骤3: 项目 ...
- 初识Nginx及其LNMP搭建
Nginx介绍 nginx www服务软件 俄罗斯人开发 开源 性能很高 web产品 大小780k c语言开发 本身是一款静态www软件,不能解析php jsp .do 最大特点 静态小文件(1m), ...
- Python编程之列表操作实例详解【创建、使用、更新、删除】
Python编程之列表操作实例详解[创建.使用.更新.删除] 这篇文章主要介绍了Python编程之列表操作,结合实例形式分析了Python列表的创建.使用.更新.删除等实现方法与相关操作技巧,需要的朋 ...
- 一种局部二值化算法:Sauvola算法
之前接触过全局二值化(OTSU算法),还有OPENCV提供的自适应二值化,最近又了解到一种新的局部二值化算法,Sauvola算法. 转载自:http://www.dididongdong.com/ar ...
- OutLook会议室预定提醒
项目组采用敏捷开发管理,每两周一个迭代.写个工具做会议室预定. 代码下载:https://download.csdn.net/download/linmilove/10547579 Appointme ...
- 【BZOJ2622】[2012国家集训队测试]深入虎穴
虎是中国传统文化中一个独特的意象.我们既会把老虎的形象用到喜庆的节日装饰画上,也可能把它视作一种邪恶的可怕的动物,例如“武松打虎”或者“三人成虎”.“不入虎穴焉得虎子”是一个对虎的威猛的形象的极好体现 ...
- html5绘图笔记纪要
在html5之前,前端是无法再html页面上动态绘制图片 html5新增了一个canvas元素,相当于一个画布,可以获取一个CanvasRenderingContext2D对象 CanvasRende ...
- Python web 面试题(一)
1.列举django的内置组件? url .view.model.template.中间件 2.列举django中间件的5个方法?以及django中间件的应用场景? process_request(s ...
- 命令行模式和Python交互模式的区别
1.命令行模式: 在Windows开始菜单选择“命令提示符”,就进入到命令行模式,它的提示符类似C:\Users\>: 2.python交互模式 在命令行模式下敲命令python,就看到类似如下 ...
- linux安装五笔拼音混输 的五笔输入法
打开终端先卸载系统的iBus sudo apt-get remove ibus 添加源sudo add-apt-repository ppa:fcitx-team/nightlysudo apt-ge ...