【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操作(改变生长点操作),一定只会会对连续的一段区间产 ...
随机推荐
- .Net Core-3.0-新闻:宣告推出.NET Core 3.0 Preview 7
ylbtech-.Net Core-3.0-新闻:宣告推出.NET Core 3.0 Preview 7 1.返回顶部 1. 今天,我们宣布推出.NET Core 3.0 Preview 7.我们已 ...
- 关于Goroutine与Channel
关于Goroutine的原理 原理上的内容比较多,比如goroutine启动的时候要执行哪些相关的操作,一点一点的补充一下. channel的基本原理 channel是go语言中的特殊的机制,既可以同 ...
- BusyBox TFTP使用(转)
开发板上使用TFTP 帮助信息: BusyBox v1.13.3 (2009-03-25 15:48:45 CST) multi-call binary Usage: tftp [OPTION]... ...
- mysql 恢复数据时中文乱码
mysql恢复数据时中文乱码,解决办法. 用source命令导入mysql数据库怎么设置中文编码 1.导出数据时指定编码在导出mysql sql执行文件的时候,指定一下编码格式: mysqldump ...
- (ROT-13解密)Flare-On4: Challenge1 login.html
说是FlareOn的逆向 倒不如说是crypto....... 题目不难 F12看源码: document.getElementById("prompt").onclick = f ...
- C#获取当前路径7中方法
//获取模块的完整路径. string path1 = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; //获取 ...
- 剑指Offer编程题(Java实现)——两个链表的第一个公共结点
题目描述: 输入两个链表,找出它们的第一个公共结点. 思路一: 设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a. ...
- 消息中间件 JMS入门
1. JMS入门 1.1消息中间件 什么是消息中间件 消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成.通过提供消息传递和消息排队模型,它可以在分布式环 ...
- Intellij IDEA奇巧妙计(不停更新)
1,在pom.xml文件中,Ctrl+Shift+Alt+U打开Manven依赖视图 2,Alt+7 查看类里面方法,变量等结构 3, Shift+Esc 收缩编译提示框 4, ctrl+r 替换本页 ...
- [LeetCode] 95. 不同的二叉搜索树 II
题目链接 : https://leetcode-cn.com/problems/unique-binary-search-trees-ii/ 题目描述: 给定一个整数 n,生成所有由 1 ... n ...