题目链接

题目描述

小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]大森林的更多相关文章

  1. [ZJOI2016]大森林(LCT)

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

  2. [ZJOI2016]大森林

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

  3. 【刷题】BZOJ 4573 [Zjoi2016]大森林

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

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

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

  5. bzoj 4573: [Zjoi2016]大森林

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

  6. P3348 [ZJOI2016]大森林

    \(\color{#0066ff}{ 题目描述 }\) 小Y家里有一个大森林,里面有n棵树,编号从1到n.一开始这些树都只是树苗,只有一个节点,标号为1.这些树都有一个特殊的节点,我们称之为生长节点, ...

  7. 洛谷P3348 [ZJOI2016]大森林 [LCT]

    传送门 刷了那么久水题之后终于有一题可以来写写博客了. 但是这题太神仙了我还没完全弄懂-- upd:写完博客之后似乎懂了. 思路 首先很容易想到\(O(n^2\log n)\)乘上\(O(\frac{ ...

  8. BZOJ4573 : [Zjoi2016]大森林

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

  9. ●洛谷P3348 [ZJOI2016]大森林

    题链: https://www.luogu.org/problemnew/show/P3348 题解: LCT,神题 首先有这么一个结论: 每次的1操作(改变生长点操作),一定只会会对连续的一段区间产 ...

随机推荐

  1. FreeBSD上安装Cassandra 3.10

    哈哈,你居然点进来了,来吧,一起吐槽FreeBSD啊,装了一上午Cassandra 3.10都没有装成功, 终于,鄙人一条 shutdown -p now 结束了FreeBSD,默默打开了CentOS ...

  2. 139、TensorFlow Serving 实现模型的部署(二) TextCnn文本分类模型

    昨晚终于实现了Tensorflow模型的部署 使用TensorFlow Serving 1.使用Docker 获取Tensorflow Serving的镜像,Docker在国内的需要将镜像的Repos ...

  3. 阶段3 1.Mybatis_03.自定义Mybatis框架_2.自定义Mybatis的分析-创建代理对象的分析

    如何创建代理对象,以及使用设计模式带来的优势 调用的组合关系 不关注的,执行JDBC那一套.第二个是解析XML,解析的技术有很多.

  4. cocos2dx基础篇(18) 数据存储CCUserDefault

    在cocos2dx中提供了一个数据存储类CCUserDefault,可以作为一个轻量级的数据库来使用.它支持五种数据bool.int.float.double.string的存储. [3.x]     ...

  5. ContextLoaderListener错误

    在web.xml中添加如下配置 <context-param> <param-name>contextConfigLocation</param-name> < ...

  6. [Web 前端] 030 ajax 是什么

    AJAX 是什么 1. AJAX 是一种"艺术" 简单地说 AJAX 是在不重新加载整个页面的情况下与服务器交换数据并更新部分网页的艺术 网上是这样说的 AJAX 指异步 Java ...

  7. Kubernetes组件及网络基础

    在前面的部分了解了Pod的创建删除 ,查看信息等.那么我们怎么去管理Pod呢?我们可以通过 ReplicationController 去管理维护 Pod. Replication Controlle ...

  8. java虚拟机学习总结之GC回收算法与GC收集器

    GC回收算法 1.标记清除算法分为标记阶段和清除阶段标记阶段:通过特定的判断方式找出无用的对象实例并将其标记清除阶段:将已标记的对象所占用的内存回收缺点:运行多次以后容易产生空间碎片,当需要一整段连续 ...

  9. 用了 10 多年的 Tomcat 居然有bug !

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 为了解决分布式链路追踪的问题,我们引入了实现OpenTracing的Jaeger来实现.然后我们为SpringBoot框架写 ...

  10. kafka连接器

    独立模式 bin/connect-standalone.sh config/connect-standalone.properties config/connect-file-source.prope ...