传送门

搞了这么长时间Splay终于可以搞LCT了,等等,什么是LCT?

$LCT$就是$Link-Cut-Tree$,是维护动态树的一个很高效的数据结构,每次修改和查询的均摊复杂度为$O(logN)$,因为那是用splay维护的,所以时间常数也会比较大..

但是相信有节操的出题人是不会恶意卡$LCT$的!

LCT的具体说明就就那篇喜闻乐见的Qtree研究,虽然我并没有怎么看懂,也许是我太弱了吧,总之和啃别人随手打的博客而言,那篇论文还是相对系统的了。

一句话概括$Link-Cut-Tree$就是Link-Cut-Tree=splay+树剖,所以下面的一些简要介绍沿用树剖和splay的用语。

具体说明几个操作;

0.$Splay$

和普通splay几乎一样,但是有一点需要注意,写在注释里了。

inline bool isroot(int node){
    if(t[node].fa==0)return 1;
    return t[t[node].fa].son[0]!=node&&t[t[node].fa].son[1]!=node;
}
inline int get(int node){return t[t[node].fa].son[1]==node;}
void rotate(int node){
    int old=t[node].fa,oldf=t[old].fa,which=get(node);
    if(!isroot(old))t[oldf].son[t[oldf].son[1]==old]=node;//先判断old是不是根再实现翻转,因为先翻转后无论如何old一定不是根
    t[old].son[which]=t[node].son[which^1];t[t[old].son[which]].fa=old;
    t[node].son[which^1]=old;t[old].fa=node;t[node].fa=oldf;
}
void splay(int x){
    int top=0;q[++top]=x;
    for(int i=x;!isroot(i);i=t[i].fa)q[++top]=t[i].fa;
    for(int i=top;i;i--)downit(q[i]);
    while(!isroot(x)){
         int old=t[x].fa,oldf=t[old].fa;
         if(!isroot(old))rotate(get(x)==get(old)?old:x);
         rotate(x);
    }
}

1.$Access$

这个操作就是把当前节点到当前树根变为重链。

具体实现的话和树剖其实有点像,跳到一条链的根,对链操作,跳掉下一条链的底。

void access(int node){
    int tmp=0;
    while(node){
        splay(node);t[node].son[1]=tmp;
        tmp=node;node=t[node].fa;
    }
}

2.$Reverse$

具体的作用就是把一个节点转到根,和splay实现区间翻转挺像的,代码实现不难理解。

void Reverse(int node){access(node);splay(node);t[node].tag^=1;}

3.$Link$

首先把一个节点转到树根,然后由这个节点向另一节点连条虚边,然后splay一下。

void Link(int noda,int nodb){
    Reverse(noda);
    t[noda].fa=nodb;
    splay(noda);
}

4.$Cut$

和上一个差不多。

void Cut(int noda,int nodb){Reverse(noda);access(nodb);splay(nodb);t[nodb].son[0]=t[noda].fa=0;}

5.$find$

int find(int node){
    access(node);splay(node);
    int tmp=node;
    while(t[tmp].son[0])tmp=t[tmp].son[0];
    return tmp;
}

6.$LCA$

void LCA(int noda,int nodb){//noda is LCA
    Reverse(nodb);access(noda);splay(noda);
}

这些操作拼接起来就是这道题的实现了。

//BZOJ 2049
//by Cydiater
//2016.9.12
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <map>
#include <ctime>
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <algorithm>
using namespace std;
#define ll long long
#define up(i,j,n)        for(int i=j;i<=n;i++)
#define down(i,j,n)        for(int i=j;i>=n;i--)
#define FILE "sdoi2008_cave"
const int MAXN=1e6+5;
const int oo=0x3f3f3f3f;
inline int read(){
    char ch=getchar();int x=0,f=1;
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int q[MAXN],top=0,head,tail,N,M,noda,nodb;
char s[15];
struct Splay{
    int son[2],fa,tag,siz;
}t[MAXN];
namespace solution{
    inline bool isroot(int node){
        if(t[node].fa==0)return 1;
        return t[t[node].fa].son[0]!=node&&t[t[node].fa].son[1]!=node;
    }
    inline int get(int node){return t[t[node].fa].son[1]==node;}
    inline void downit(int node){
        if(t[node].tag){
            int leftson=t[node].son[0],rightson=t[node].son[1];
            t[leftson].tag^=1;t[rightson].tag^=1;
            swap(t[node].son[0],t[node].son[1]);
            t[node].tag=0;
        }
    }
    void rotate(int node){
        int old=t[node].fa,oldf=t[old].fa,which=get(node);
        if(!isroot(old))t[oldf].son[t[oldf].son[1]==old]=node;//先判断old是不是根再实现翻转,因为先翻转后无论如何old一定不是根
        t[old].son[which]=t[node].son[which^1];t[t[old].son[which]].fa=old;
        t[node].son[which^1]=old;t[old].fa=node;t[node].fa=oldf;
    }
    void splay(int x){
        int top=0;q[++top]=x;
        for(int i=x;!isroot(i);i=t[i].fa)q[++top]=t[i].fa;
        for(int i=top;i;i--)downit(q[i]);
        while(!isroot(x)){
            int old=t[x].fa,oldf=t[old].fa;
            if(!isroot(old))rotate(get(x)==get(old)?old:x);
            rotate(x);
        }
    }
    void access(int node){
        int tmp=0;
        while(node){
            splay(node);t[node].son[1]=tmp;
            tmp=node;node=t[node].fa;
        }
    }
    void Reverse(int node){access(node);splay(node);t[node].tag^=1;}
    void Link(int noda,int nodb){
        Reverse(noda);
        t[noda].fa=nodb;
        splay(noda);
    }
    void Cut(int noda,int nodb){Reverse(noda);access(nodb);splay(nodb);t[nodb].son[0]=t[noda].fa=0;}
    int find(int node){
        access(node);splay(node);
        int tmp=node;
        while(t[tmp].son[0])tmp=t[tmp].son[0];
        return tmp;
    }
}
int main(){
    //freopen(FILE".in","r",stdin);
    //freopen(FILE".out","w",stdout);
    freopen("input.in","r",stdin);
    using namespace solution;
    N=read();M=read();
    while(M--){
        scanf("%s",s);noda=read();nodb=read();
        if(s[0]=='C')Link(noda,nodb);
        if(s[0]=='D')Cut(noda,nodb);
        if(s[0]=='Q')puts(find(noda)==find(nodb)?"Yes":"No");
    }
    return 0;
}

BZOJ2049: [Sdoi2008]Cave 洞穴勘测 Link-Cut-Tree 模板题的更多相关文章

  1. bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

    link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...

  2. BZOJ2049 SDOI2008 Cave 洞穴勘测 【LCT】

    BZOJ2049 SDOI2008 Cave 洞穴勘测 Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分 ...

  3. 【LCT】BZOJ2049 [SDOI2008]Cave 洞穴勘测

    2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 10059  Solved: 4863[Submit ...

  4. [BZOJ2049][Sdoi2008]Cave 洞穴勘测 LCT模板

    2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 9705  Solved: 4674[Submit] ...

  5. [bzoj2049][Sdoi2008]Cave 洞穴勘测_LCT

    Cave 洞穴勘测 bzoj-2049 Sdoi-2008 题目大意:维护一个数据结构,支持森林中加边,删边,求两点连通性.n个点,m个操作. 注释:$1\le n\le 10^4$,$1\le m\ ...

  6. [BZOJ2049] [SDOI2008] Cave 洞穴勘测 (LCT)

    Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...

  7. BZOJ2049——[Sdoi2008]Cave 洞穴勘测

    1.题目大意:就是一个动态维护森林联通性的题 2.分析:lct模板题 #include <stack> #include <cstdio> #include <cstdl ...

  8. [bzoj2049][Sdoi2008]Cave 洞穴勘测——lct

    Brief Description 给定一个森林,您需要支持两种操作: 链接两个节点. 断开两个节点之间的链接. Algorithm Design 对于树上的操作,我们现在已经有了树链剖分可以处理这些 ...

  9. bzoj2049: [Sdoi2008]Cave 洞穴勘测 lct裸题

    题意:三种操作一种摧毁一条边,一种链接一条边,一种查询两个点是否联通 题解:lct的link和cut即可 /********************************************** ...

随机推荐

  1. xml序列化及反序列化.net对象

    序列化一个类通常添加[XmlRoot("根节点名字")] 找到要序列化的内容 对要序列化的类添加 [Serializable]属性用于序列化 对于要序列化的字段添加  [XmlEl ...

  2. [BZOJ1061][Noi 2008]志愿者招募(网络流)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1061 分析: 神题不解释,只能欣赏:https://www.byvoid.com/bl ...

  3. android之自定义广播

    布局文件 点击按钮发送广播 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmln ...

  4. SVN——配置和安装

    SVN安装步骤: 所有安装文件下载地址:http://pan.baidu.com/s/1bocNTDl 一.安装01----VisualSVN-Server-3.4.2-x64.msi 文件 直接下一 ...

  5. Beta版本冲刺———第五天

    会议照片: 项目燃尽图: 1.项目进展: 困难:基本计划中增加的功能已经完成,但是在"如何保存每次游戏的分数,并将其排序列在排行榜中"遇到麻烦,现在小组都在一起协商攻克中.

  6. js原型继承的几种方式

    1. 原型链继承 2,构造函数继承(对象冒充继承) 3,组合继承(原型链继承+构造函数继承) 4,原型式继承 5. 寄生组合式继承 一.原型链继承 function Show(){ this.name ...

  7. Java--笔记(2)

    11.根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态. 死锁的四个必要条件: 1)互斥条件(Mutual ...

  8. ASP.NET杂谈-一切都从web.config说起(2)(ConfigSections详解-下)

    还是接着上一篇说起,在上两篇中主要和大家探讨了ConfigSection的几种常用形式,并举例几个例子说明了一下.其实它们主要都是继承System.Configuration.Configuratio ...

  9. jQuery常用的元素查找方法总结

    $("#myELement")    选择id值等于myElement的元素,id值不能重复在文档中只能有一个id值是myElement所以得到的是唯一的元素 $("di ...

  10. mysql-拼接字段concat,concat_ws函数

    Mysql的查询结果行字段拼接,可以用下面两个函数实现: 1. concat函数 mysql') from test ; +---------------------+ ') | +--------- ...