原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ195.html

题解

  首先询问都可以放到最后处理。

  对于操作,我们把它差分一下离线下来。

  现在的问题就是从第一棵树到第 n 棵树扫一遍,并不断维护树的形态。

  容易感受到这棵树会有删节点之类的操作,所以自然想到 LCT 。

  但是要涉及一个节点的一些子节点换父亲的时候LCT就GG了。

  解决这个问题的办法是建立虚点。虚点权值为 0 ,实点权值为 1,于是我们要维护链上点权和。

  建虚点的规则是:

  对于每一个操作 1 都建立一个虚点。即:将区间 [L,R] 的生长节点换成 x 的时候,建一个虚点,这个虚点的父亲 在 [L,R] 中是 x ,否则是上一个生长节点对应的虚点。

  对于每一个操作 2 ,直接把他连到上一个生长节点所对应的虚点上去就好了。

  我们发现我们维护的LCT要获取父亲信息,所以不方便换根。那就不换了!

  那么怎么求两点距离呢?

  dis(x,y) = depth[x]+depth[y] - 2*depth[LCA(x,y)]

  LCT怎么求LCA 呢?见代码中的函数 Ask()

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

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=200005*2;
int n,m,oc=0;
struct Opt{
int t,lev,x,y;
Opt(){}
Opt(int _t,int _lev,int _x,int _y){
t=_t,lev=_lev,x=_x,y=_y;
}
}o[N*3];
bool cmpo(Opt a,Opt b){
return a.t!=b.t?a.t<b.t:a.lev<b.lev;
}
int lv[N],rv[N],id[N],ans[N];
namespace lct{
int size[N],val[N],fa[N],son[N][2];
int n;
void pushup(int x){
size[x]=size[son[x][0]]+val[x]+size[son[x][1]];
}
int Add(int v){
val[++n]=v,pushup(n);
return n;
}
void init(){
n=0;
id[1]=Add(1);
}
int isroot(int x){
return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
}
int wson(int x){
return son[fa[x]][1]==x;
}
void rotate(int x){
if (isroot(x))
return;
int y=fa[x],z=fa[y],L=wson(x),R=L^1;
if (!isroot(y))
son[z][wson(y)]=x;
fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
son[y][L]=son[x][R],son[x][R]=y;
pushup(y),pushup(x);
}
void splay(int x){
for (int y=fa[x];!isroot(x);rotate(x),y=fa[x])
if (!isroot(y))
rotate(wson(x)==wson(y)?y:x);
}
int access(int x){
int t;
for (t=0;x;t=x,x=fa[x])
splay(x),son[x][1]=t,pushup(x);
return t;
}
void refather(int x,int y){
access(x),splay(x),son[x][0]=fa[son[x][0]]=0,pushup(x);
fa[x]=y;
}
int Ask(int x,int y){
int ans=0;
access(x),splay(x),ans=size[x];
int z=access(y);
splay(y),ans+=size[y];
access(z),splay(z),ans-=size[z]*2;
return ans;
}
}
int main(){
n=read(),m=read();
lct::init();
lv[1]=1,rv[1]=n;
lct::refather(lct::Add(0),1);
int pre=2,cnt=1,q=0;
for (int i=1;i<=m;i++){
int type=read();
if (type==0){
int L=read(),R=read();
cnt++,lv[cnt]=L,rv[cnt]=R;
o[++oc]=Opt(1,i,id[cnt]=lct::Add(1),pre);
}
else if (type==1){
int L=read(),R=read(),x=read();
L=max(L,lv[x]),R=min(R,rv[x]);
if (L<=R){
int now=lct::Add(0);
if (L>1)
lct::refather(now,pre);
o[++oc]=Opt(L,i,now,id[x]);
o[++oc]=Opt(R+1,i,now,pre);
pre=now;
}
}
else {
int k=read(),x=read(),y=read();
o[++oc]=Opt(k,(++q)+m,id[x],id[y]);
}
}
sort(o+1,o+oc+1,cmpo);
for (int i=1,j=1;i<=n;i++){
while (j<=oc&&o[j].t<=i){
int k=o[j].lev,x=o[j].x,y=o[j].y;
if (k<=m)
lct::refather(x,y);
else
ans[k-m]=lct::Ask(x,y);
j++;
}
}
for (int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}

  

UOJ#195. 【ZJOI2016】大♂森林 LCT的更多相关文章

  1. [ZJOI2016]大森林(LCT)

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

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

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

  3. bzoj 4573: [Zjoi2016]大森林 lct splay

    http://www.lydsy.com/JudgeOnline/problem.php?id=4573 http://blog.csdn.net/lych_cys/article/details/5 ...

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

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

  5. [ZJOI2016]大森林

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

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

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

  7. P3348 [ZJOI2016]大森林

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

  8. bzoj 4573: [Zjoi2016]大森林

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

  9. 【LuoguP3348】[ZJOI2016]大森林

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

随机推荐

  1. Java【第四篇】基本语法之--循环

    循环语句功能 在循环条件满足的情况下,反复执行特定代码 循环语句的四个组成部分 初始化部分(init_statement)循环条件部分(test_exp) 循环体部分(body_statement) ...

  2. BZOJ3864: Hero meet devil(dp套dp)

    Time Limit: 8 Sec  Memory Limit: 128 MBSubmit: 397  Solved: 206[Submit][Status][Discuss] Description ...

  3. 洛谷P2120 [ZJOI2007]仓库建设 斜率优化DP

    做的第一道斜率优化\(DP\)QwQ 原题链接1/原题链接2 首先考虑\(O(n^2)\)的做法:设\(f[i]\)表示在\(i\)处建仓库的最小费用,则有转移方程: \(f[i]=min\{f[j] ...

  4. TensorFlow深度学习,一篇文章就够了

    http://blog.jobbole.com/105602/ 作者: 陈迪豪,就职小米科技,深度学习工程师,TensorFlow代码提交者. TensorFlow深度学习框架 Google不仅是大数 ...

  5. jdbc增删改查进行封装

    jdbc封装 1 dao (代码分层) com.aaa.dao 存放dao相关的类型 例如 StudentDAOImpl 处理 数据库的链接 存取数据 com.aaa.servlet 存放servle ...

  6. URL和URI以及两者的区别和联系

    1.url: 统一资源定位符(Uniform Resource Locator,URL)是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址.互联网上的每个文件都有一 ...

  7. KNN算法的实现

    K近邻(KNN)算法简介 KNN是通过测量不同特征值之间的距离进行分类.它的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别,其 ...

  8. python 网络编程之TCP传输&粘包传输

    只有TCP有粘包现象,UDP永远不会粘包. 所谓粘包问题主要还是C/S两端数据传输时 因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的 根本原因:粘包是由TCP协议本身造成的,T ...

  9. 一个select元素自定义设计的新思路:appearance: none之后利用<>符号制造小箭头

    最近工作时解决了一个前端小问题(如下图所示):在Safari中,select的控件之上有不和谐的灰色部分. 刚开始时我以为是backgrand或是border设置不当之类产生的问题,在搜索了很久之后终 ...

  10. c# mvc 在控制器中动态解析cshtml文件并获取对应的html代码

    public static string GetViewHtml(ControllerContext context, string viewName, Object param) { if (str ...