【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操作(改变生长点操作),一定只会会对连续的一段区间产 ...
随机推荐
- handsonetable+vue 表格在线编辑
<template> <div> <div id="example-container" class="wrapper"> ...
- 【python】集合 list差集|并集|交集
两个list差集 list(set(b).difference(set(a))) # b中有而a中没有的 示例: a=[1,2,3] b=[2,3] list(set(a).difference(se ...
- db4o发布7.2,出现.NET 3.5版本,支持LINQ
db4o发布7.2,出现.NET 3.5版本,支持LINQ Db4Object刚刚发布了db4o的7.2beta,除了以前支持如下的平台:.NET 1.1,.NET 2.0,Mono外,现在还支持 ...
- 【Airtest】由于Airtest中long_click无法实现长按,教你如何在Airtest中实现长按的方法
Airtest中我们想要实现长按操作,poco中有一个方法long_click,但是实际使用了一下,发现并没有卵用,仍然是单击操作,如下图 那我们要如何进行长按操作呢?其实可以利用swipe实现,以长 ...
- Go语言入门篇-项目常见用法&语法
一.导入包用法: //_表示仅执行该包下的init函数(不需要整个包导入) import _ "git.xx.xx/baases/identity/cachain/version" ...
- if——while表达式详解
①while循环的表达式是循环进行的条件,用作循环条件的表达式中一般至少包括一个能够改变表达式的变量,这个变量称为循环变量 ②当表达式的值为真(非零)(非空)时,执行循环体:为假(0)时,则循环结束 ...
- Nginx跨域问题
Nginx跨域无法访问,通常报错: Failed to load http://172.18.6.30:8086/CityServlet: No 'Access-Control-Allow-Origi ...
- kafka 安装教程
安装详述: https://www.jianshu.com/p/596f107e901a 3.0:运行:cd 到: D:\Installed_software\Professional\kafka_2 ...
- mac下安装php zookeeper扩展
安装步骤 php-zookeeper依赖libzookeeper,所以需要先安装libzookeeper 安装libzookeeper cd /usr/local/src/ wget http://m ...
- windows10操作系统上使用virtualenv虚拟环境
前提win10上已经安装了Python环境! virtualenv库的使用: 安装 如果win10上同时安装了Python2和python3的安装virtualenv时用; Python2:pip i ...