模板库(五) - 数据结构模板

写在前面

模板库”这一系列文章用来复习 O I OI 模板
由于时间原因,作者无法一一亲自调试其中的程序,也因如此,有一部分程序来自于互联网,如果您觉得这侵犯了您的合法权益,请联系 ( Q Q 2068926345 ) (QQ2068926345) 删除。
对于给您造成的不便和困扰,我表示深深的歉意。
本系列文章仅用于学习,禁止任何人或组织用于商业用途。
本系列文章中,标记*的为选学算法,在 N O I P NOIP 中较少涉及。

数据结构

队列

【简介】

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端 ( h e a d ) (head) 进行删除操作,而在表的后端 ( t a i l ) (tail) 进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

#include<cstdio>
#include<queue>
using namespace std;
queue<int> q;
int n,opt,k;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&opt);
        if(opt==1) scanf("%d",&k),q.push(k);
        else if(opt==2) q.pop();
        else printf("%d\n",q.front()); 
    } 
    return 0;
}

复杂度(入队,出队,访问队首) Θ ( 1 ) \Theta(1)


【简介】

( s t a c k ) (stack) 又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

【代码实现】
#include<cstdio>
#include<stack>
using namespace std;
int n,opt,k;
stack<int> s;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&opt);
        if(opt==1) scanf("%d",&k),s.push(k);
        else if(opt==2) s.pop();
        else printf("%d\n",s.top());
    }
    return 0; 
}

复杂度(入栈,出栈,访问栈顶) Θ ( 1 ) \Theta(1)


【简介】

堆( h e a p heap )是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。

【代码实现】

二叉堆

#include<cstdio>
#include<queue>
#include<cctype>
using namespace std;
priority_queue<int,vector<int>,greater<int> > q;
inline int read(){
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f|=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?-x:x;
}
int main(){
    int m=read();
    for(int i=1;i<=m;i++){
        int a=read();
        if(a==1){
            int b=read();
            q.push(b); 
        }
        else if(a==2) printf("%d\n"q.top());
        else q.pop(); 
    }
    return 0;
}

*斐波那契堆

#ifndef _FIBONACCI_TREE_HPP_
#define _FIBONACCI_TREE_HPP_
#include <iomanip>
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;
template <class T>
class FibNode {
        public:
        T key;                // 关键字(键值)
        int degree;            // 度数
        FibNode<T> *left;    // 左兄弟
        FibNode<T> *right;    // 右兄弟
        FibNode<T> *child;    // 第一个孩子节点
        FibNode<T> *parent;    // 父节点
        bool marked;        // 是否被删除第一个孩子
        FibNode(T value):key(value), degree(0), marked(false), 
            left(NULL),right(NULL),child(NULL),parent(NULL) {
            key    = value;
            degree = 0;
            marked = false;
            left   = this;
            right  = this;
            parent = NULL;
            child  = NULL;
        }
};
template <class T>
class FibHeap {
    private:
        int keyNum;         // 堆中节点的总数
        int maxDegree;      // 最大度
        FibNode<T> *min;    // 最小节点(某个最小堆的根节点)
        FibNode<T> **cons;    // 最大度的内存区域
    public:
        FibHeap();
        ~FibHeap();
        // 新建key对应的节点,并将其插入到斐波那契堆中
        void insert(T key);
        // 移除斐波那契堆中的最小节点
        void removeMin();
        // 将other合并到当前堆中
        void combine(FibHeap<T> *other);
        // 获取斐波那契堆中最小键值,并保存到pkey中;成功返回true,否则返回false。
        bool minimum(T *pkey);
        // 将斐波那契堆中键值oldkey更新为newkey
        void update(T oldkey, T newkey);
        // 删除键值为key的节点
        void remove(T key);
        // 斐波那契堆中是否包含键值key
        bool contains(T key);
        // 打印斐波那契堆
        void print();
        // 销毁
        void destroy();
    private:
        // 将node从双链表移除
        void removeNode(FibNode<T> *node);
        // 将node堆结点加入root结点之前(循环链表中)
        void addNode(FibNode<T> *node, FibNode<T> *root);
        // 将双向链表b链接到双向链表a的后面
        void catList(FibNode<T> *a, FibNode<T> *b);
        // 将节点node插入到斐波那契堆中
        void insert(FibNode<T> *node);
        // 将"堆的最小结点"从根链表中移除,
        FibNode<T>* extractMin();
        // 将node链接到root根结点
        void link(FibNode<T>* node, FibNode<T>* root);
        // 创建consolidate所需空间
        void makeCons();
        // 合并斐波那契堆的根链表中左右相同度数的树
        void consolidate();
        // 修改度数
        void renewDegree(FibNode<T> *parent, int degree);
        // 将node从父节点parent的子链接中剥离出来,并使node成为"堆的根链表"中的一员。
        void cut(FibNode<T> *node, FibNode<T> *parent);
        // 对节点node进行"级联剪切"
        void cascadingCut(FibNode<T> *node) ;
        // 将斐波那契堆中节点node的值减少为key
        void decrease(FibNode<T> *node, T key);
        // 将斐波那契堆中节点node的值增加为key
        void increase(FibNode<T> *node, T key);
        // 更新斐波那契堆的节点node的键值为key
        void update(FibNode<T> *node, T key);
        // 在最小堆root中查找键值为key的节点
        FibNode<T>* search(FibNode<T> *root, T key);
        // 在斐波那契堆中查找键值为key的节点
        FibNode<T>* search(T key);
        // 删除结点node
        void remove(FibNode<T> *node);
        // 销毁斐波那契堆
        void destroyNode(FibNode<T> *node);
        // 打印"斐波那契堆"
        void print(FibNode<T> *node, FibNode<T> *prev, int direction);
};
/* 
 * 构造函数
 */
template <class T>
FibHeap<T>::FibHeap()
{
    keyNum = 0;
    maxDegree = 0;
    min = NULL;
    cons = NULL;
}
/* 
 * 析构函数
 */
template <class T>
FibHeap<T>::~FibHeap() 
{
}
/* 
 * 将node从双链表移除
 */
template <class T>
void FibHeap<T>::removeNode(FibNode<T> *node)
{
    node->left->right = node->right;
    node->right->left = node->left;
}
/*
 * 将node堆结点加入root结点之前(循环链表中)
 *   a …… root
 *   a …… node …… root
*/
template <class T>
void FibHeap<T>::addNode(FibNode<T> *node, FibNode<T> *root)
{
    node->left        = root->left;
    root->left->right = node;
    node->right       = root;
    root->left        = node;
}
/*
 * 将节点node插入到斐波那契堆中
 */
template <class T>
void FibHeap<T>::insert(FibNode<T> *node)
{
    if (keyNum == 0)
        min = node;
    else
       {
        addNode(node, min);
        if (node->key < min->key)
            min = node;
    }
    keyNum++;
}
/* 
 * 新建键值为key的节点,并将其插入到斐波那契堆中
 */
template <class T>
void FibHeap<T>::insert(T key)
{
    FibNode<T> *node;
    node = new FibNode<T>(key);
    if (node == NULL)
        return ;
    insert(node);
}
/*
 * 将双向链表b链接到双向链表a的后面
 *
 * 注意: 此处a和b都是双向链表
 */
template <class T>
void FibHeap<T>::catList(FibNode<T> *a, FibNode<T> *b)
{
    FibNode<T> *tmp;
    tmp            = a->right;
    a->right       = b->right;
    b->right->left = a;
    b->right       = tmp;
    tmp->left      = b;
}
/*
 * 将other合并到当前堆中
 */
template <class T>
void FibHeap<T>::combine(FibHeap<T> *other)
{
    if (other==NULL)
        return ;
    if(other->maxDegree > this->maxDegree)
        swap(*this, *other);
    if((this->min) == NULL)                // this无"最小节点"
    {
        this->min = other->min;
        this->keyNum = other->keyNum;
        free(other->cons);
        delete other;
    }
    else if((other->min) == NULL)           // this有"最小节点" && other无"最小节点"
    {
        free(other->cons);
        delete other;
    }                                       // this有"最小节点" && other有"最小节点"
    else
    {
        // 将"other中根链表"添加到"this"中
        catList(this->min, other->min);
        if (this->min->key > other->min->key)
            this->min = other->min;
        this->keyNum += other->keyNum;
        free(other->cons);
        delete other;
    }
}
/*
 * 将"堆的最小结点"从根链表中移除,
 * 这意味着"将最小节点所属的树"从堆中移除!
 */
template <class T>
FibNode<T>* FibHeap<T>::extractMin()
{
    FibNode<T> *p = min;
    if (p == p->right)
        min = NULL;
    else
    {
        removeNode(p);
        min = p->right;
    }
    p->left = p->right = p;
    return p;
}
/*
 * 将node链接到root根结点
 */
template <class T>
void FibHeap<T>::link(FibNode<T>* node, FibNode<T>* root)
{
    // 将node从双链表中移除
    removeNode(node);
    // 将node设为root的孩子
    if (root->child == NULL)
        root->child = node;
    else
        addNode(node, root->child);
    node->parent = root;
    root->degree++;
    node->marked = false;
}
/* 
 * 创建consolidate所需空间
 */
template <class T>
void FibHeap<T>::makeCons()
{
    int old = maxDegree;
    // 计算log2(keyNum),"+1"意味着向上取整!
    // ex. log2(13) = 3,向上取整为3+1=4。
    maxDegree = (log(keyNum)/log(2.0)) + 1;
    if (old >= maxDegree)
        return ;
    // 因为度为maxDegree可能被合并,所以要maxDegree+1
    cons = (FibNode<T> **)realloc(cons, 
            sizeof(FibHeap<T> *) * (maxDegree + 1));
}
/* 
 * 合并斐波那契堆的根链表中左右相同度数的树
 */
template <class T>
void FibHeap<T>::consolidate()
{
    int i, d, D;
    FibNode<T> *x, *y, *tmp;
    makeCons();//开辟哈希所用空间
    D = maxDegree + 1;
    for (i = 0; i < D; i++)
        cons[i] = NULL;
    // 合并相同度的根节点,使每个度数的树唯一
    while (min != NULL)
    {
        x = extractMin();                // 取出堆中的最小树(最小节点所在的树)
        d = x->degree;                    // 获取最小树的度数
        // cons[d] != NULL,意味着有两棵树(x和y)的"度数"相同。
        while (cons[d] != NULL)
        {
            y = cons[d];                // y是"与x的度数相同的树" 
            if (x->key > y->key)        // 保证x的键值比y小
                swap(x, y);
            link(y, x);    // 将y链接到x中
            cons[d] = NULL;
            d++;
        }
        cons[d] = x;
    }
    min = NULL;
    // 将cons中的结点重新加到根表中
    for (i=0; i<D; i++)
    {
        if (cons[i] != NULL)
        {
            if (min == NULL)
                min = cons[i];
            else
            {
                addNode(cons[i], min);
                if ((cons[i])->key < min->key)
                    min = cons[i];
            }
        }
    }
}
/*
 * 移除最小节点
 */
template <class T>
void FibHeap<T>::removeMin()
{
    if (min==NULL)
        return ;
    FibNode<T> *child = NULL;
    FibNode<T> *m = min;
    // 将min每一个儿子(儿子和儿子的兄弟)都添加到"斐波那契堆的根链表"中
    while (m->child != NULL)
    {
        child = m->child;
        removeNode(child);
        if (child->right == child)
            m->child = NULL;
        else
            m->child = child->right;
        addNode(child, min);
        child->parent = NULL;
    }
    // 将m从根链表中移除
    removeNode(m);
    // 若m是堆中唯一节点,则设置堆的最小节点为NULL;
    // 否则,设置堆的最小节点为一个非空节点(m->right),然后再进行调节。
    if (m->right == m)
        min = NULL;
    else
    {
        min = m->right;
        consolidate();
    }
    keyNum--;
    delete m;
}
/*
 * 获取斐波那契堆中最小键值,并保存到pkey中;成功返回true,否则返回false。
 */
template <class T>
bool FibHeap<T>::minimum(T *pkey)
{
    if (min==NULL || pkey==NULL)
        return false;
    *pkey = min->key;
    return true;
}
/* 
 * 修改度数
 */
template <class T>
void FibHeap<T>::renewDegree(FibNode<T> *parent, int degree)
{
    parent->degree -= degree;
    if (parent-> parent != NULL)
        renewDegree(parent->parent, degree);
}
/* 
 * 将node从父节点parent的子链接中剥离出来,
 * 并使node成为"堆的根链表"中的一员。
 */
template <class T>
void FibHeap<T>::cut(FibNode<T> *node, FibNode<T> *parent)
{
    removeNode(node);
    renewDegree(parent, node->degree);
    // node没有兄弟
    if (node == node->right) 
        parent->child = NULL;
    else 
        parent->child = node->right;
    node->parent = NULL;
    node->left = node->right = node;
    node->marked = false;
    // 将"node所在树"添加到"根链表"中
    addNode(node, min);
}
/* 
 * 对节点node进行"级联剪切"
 *
 * 级联剪切:如果减小后的结点破坏了最小堆性质,
 *     则把它切下来(即从所在双向链表中删除,并将
 *     其插入到由最小树根节点形成的双向链表中),
 *     然后再从"被切节点的父节点"到所在树根节点递归执行级联剪枝
 */
template <class T>
void FibHeap<T>::cascadingCut(FibNode<T> *node) 
{
    FibNode<T> *parent = node->parent;
    if (parent != NULL)
    {
        if (node->marked == false) 
            node->marked = true;
        else
        {
            cut(node, parent);
            cascadingCut(parent);
        }
    }
}
/* 
 * 将斐波那契堆中节点node的值减少为key
 */
template <class T>
void FibHeap<T>::decrease(FibNode<T> *node, T key)
{
    FibNode<T> *parent;
    if (min==NULL ||node==NULL) 
        return ;
    if ( key>=node->key)
    {
        cout << "decrease failed: the new key(" << key <<") "
             << "is no smaller than current key(" << node->key <<")" << endl;
        return ;
    }
    node->key = key;
    parent = node->parent;
    if (parent!=NULL && node->key < parent->key)
    {
        // 将node从父节点parent中剥离出来,并将node添加到根链表中
        cut(node, parent);
        cascadingCut(parent);
    }
    // 更新最小节点
    if (node->key < min->key)
        min = node;
}
/* 
 * 将斐波那契堆中节点node的值增加为key
 */
template <class T>
void FibHeap<T>::increase(FibNode<T> *node, T key)
{
    FibNode<T> *child, *parent, *right;
    if (min==NULL ||node==NULL) 
        return ;
    if (key <= node->key)
    {
        cout << "increase failed: the new key(" << key <<") " 
             << "is no greater than current key(" << node->key <<")" << endl;
        return ;
    }
    // 将node每一个儿子(不包括孙子,重孙,...)都添加到"斐波那契堆的根链表"中
    while (node->child != NULL)
    {
        child = node->child;
        removeNode(child);               // 将child从node的子链表中删除
        if (child->right == child)
            node->child = NULL;
        else
            node->child = child->right;
        addNode(child, min);       // 将child添加到根链表中
        child->parent = NULL;
    }
    node->degree = 0;
    node->key = key;
    // 如果node不在根链表中,
    //     则将node从父节点parent的子链接中剥离出来,
    //     并使node成为"堆的根链表"中的一员,
    //     然后进行"级联剪切"
    // 否则,则判断是否需要更新堆的最小节点
    parent = node->parent;
    if(parent != NULL)
    {
        cut(node, parent);
        cascadingCut(parent);
    }
    else if(min == node)
    {
        right = node->right;
        while(right != node)
        {
            if(node->key > right->key)
                min = right;
            right = right->right;
        }
    }
}
/* 
 * 更新斐波那契堆的节点node的键值为key
 */
template <class T>
void FibHeap<T>::update(FibNode<T> *node, T key)
{
    if(key < node->key)
        decrease(node, key);
    else if(key > node->key)
        increase(node, key);
    else
        cout << "No need to update!!!" << endl;
}
template <class T>
void FibHeap<T>::update(T oldkey, T newkey)
{
    FibNode<T> *node;
    node = search(oldkey);
    if (node!=NULL)
        update(node, newkey);
}
/*
 * 在最小堆root中查找键值为key的节点
 */
template <class T>
FibNode<T>* FibHeap<T>::search(FibNode<T> *root, T key)
{
    FibNode<T> *t = root;    // 临时节点
    FibNode<T> *p = NULL;    // 要查找的节点
    if (root==NULL)
        return root;
    do
    {
        if (t->key == key)
        {
            p = t;
            break;
        } 
        else
        {
            if ((p = search(t->child, key)) != NULL) 
                break;
        }    
        t = t->right;
    } while (t != root);
    return p;
}
/*
 * 在斐波那契堆中查找键值为key的节点
 */
template <class T>
FibNode<T>* FibHeap<T>::search(T key)
{
    if (min==NULL)
        return NULL;
    return search(min, key);
}
/*
 * 在斐波那契堆中是否存在键值为key的节点。
 * 存在返回true,否则返回false。
 */
template <class T>
bool FibHeap<T>::contains(T key)
{
    return search(key)!=NULL ? true: false;
}
/*
 * 删除结点node
 */
template <class T>
void FibHeap<T>::remove(FibNode<T> *node)
{
    T m = min->key-1;
    decrease(node, m-1);
    removeMin();
}
template <class T>
void FibHeap<T>::remove(T key)
{
    FibNode<T> *node;
    if (min==NULL)
        return ;
    node = search(key);
    if (node==NULL)
        return ;
    remove(node);
}
/* 
 * 销毁斐波那契堆
 */
template <class T>
void FibHeap<T>::destroyNode(FibNode<T> *node)
{
    FibNode<T> *start = node;
    if(node == NULL)
        return;
    do {
        destroyNode(node->child);
        // 销毁node,并将node指向下一个
        node = node->right;
        delete node->left;
    } while(node != start);
}
template <class T>
void FibHeap<T>::destroy()
{
    destroyNode(min);
    free(cons);
}
/*
 * 打印"斐波那契堆"
 *
 * 参数说明:
 *     node       -- 当前节点
 *     prev       -- 当前节点的前一个节点(父节点or兄弟节点)
 *     direction  --  1,表示当前节点是一个左孩子;
 *                    2,表示当前节点是一个兄弟节点。
 */
template <class T>
void FibHeap<T>::print(FibNode<T> *node, FibNode<T> *prev, int direction)
{
    FibNode<T> *start=node;
    if (node==NULL)
        return ;
    do
    {
        if (direction == 1)
            cout << setw(8) << node->key << "(" << node->degree << ") is "<< setw(2) << prev->key << "'s child" << endl;
        else
            cout << setw(8) << node->key << "(" << node->degree << ") is "<< setw(2) << prev->key << "'s next" << endl;
        if (node->child != NULL)
            print(node->child, node, 1);
        // 兄弟节点
        prev = node;
        node = node->right;
        direction = 2;
    } while(node != start);
}
template <class T>
void FibHeap<T>::print()
{
    int i=0;
    FibNode<T> *p;
    if (min==NULL)
        return ;
    cout << "== 斐波那契堆的详细信息: ==" << endl;
    p = min;
    do {
        i++;
        cout << setw(2) << i << ". " << setw(4) << p->key << "(" << p->degree << ") is root" << endl;
        print(p->child, p, 1);
        p = p->right;
    } while (p != min);
    cout << endl;
}
#endif

*左偏堆

//bzoj 1455
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1000001;
int n,m; int a[N]; bool die[N];
int l[N],r[N],fa[N],dep[N];
inline int read(){
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f|=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?-x:x;
}
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int merge(int x,int y){
    if(!x)return y;
    if(!y)return x;
    if(a[x]>a[y]) swap(x,y);
    r[x]=merge(r[x],y);
    if(dep[r[x]]>dep[l[x]])swap(l[x],r[x]);
    dep[x]=dep[r[x]]+1;
    return x;
}
void work(){
    char ch[10]; int x,y;
    for(int i=1;i<=m;i++){
        scanf("%s",ch);
        if(ch[0]=='M') {
            x=read(),y=read();
            if (die[x]||die[y]) continue;
            int f1=find(x),f2=find(y);
            if(f1!=f2){
                int t=merge(f1,f2);
                fa[f2]=fa[f1]=t;
            }
        }
        else {
            x=read();
            if(die[x])printf("0\n");
            else{
                int f1=find(x);die[f1]=1;
                printf("%d\n",a[f1]);
                fa[f1]=merge(l[f1],r[f1]);
                fa[fa[f1]]=fa[f1];
            }
        }
    }
    return;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read(),fa[i]=i;
    m=read();
    work();
    return 0;
}

*配对堆

//Luogu P3371
#include<bits/stdc++.h>
using namespace std;
const int M=2e6+5;
int n,root,id,tot,top,val[M],head[M],nxt[M],to[M],dad[M];
queue<int>dui;
void add(int x,int y){
    nxt[++id]=head[x],head[x]=id,to[id]=y,dad[y]=x;
}
int _new(int x){
    val[++tot]=x;
    return tot;
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(val[x]<val[y]){
        add(x,y);
        return x;
    }
    else{
        add(y,x);
        return y;
    }
}
void pop(){
    int t;
    for(int i=head[root]; i; i=nxt[i])if(dad[to[i]]==root)dui.push(to[i]),dad[to[i]]=0;
    while(dui.size()>1){
        t=dui.front();
        dui.pop();
        dui.push(merge(dui.front(),t));
        dui.pop();
    }
    if(dui.size())root=dui.front(),dui.pop();
    else root=0;
}
int main(){
    scanf("%d",&n);
    int op,x;
    for(int i=1; i<=n; ++i){
        scanf("%d",&op);
        if(op==1)scanf("%d",&x),root=merge(_new(x),root);
        else if(op==2)printf("%d\n",val[root]);
        else pop();
    }
}

并查集

【简介】

并查集,在一些有 N N 个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中 。

并查集是一种树型的数据结构,用于处理一些不相交集合 ( D i s j o i n t (Disjoint S e t s ) Sets) 的合并及查询问题。常常在使用中以森林来表示。

【代码实现】

朴素并查集(带路径压缩)

//https://www.luogu.org/problemnew/show/P3367
#include<iostream>
#include<cstdio>
#include<ctype.h>
using namespace std;
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f|=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}
int fa[200007];
int find(int x){
    if(fa[x]!=x) return fa[x]=find(fa[x]);
    else return x;
}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++){
    int o=read(),a=read(),b=read();
        if(o==1){
            a=find(a),b=find(b);
            if(a!=b) fa[b]=a;
        }
        else printf(find(a)==find(b)?"Y\n":"N\n");
    }
    return 0;
}

M e r g e S e t ( ) 随机合并版MergeSet()函数:

void MergeSet(int x,int y){
    srand(time(NULL)); 
    x=find(x),y=find(y);
    if(x!=y){
        if(rand()&1) fa[x]=y;
        else fa[y]=x;
    }
    return;
} 

M e r g e S e t ( ) 按秩合并版MergeSet()函数:

void MergeSet(int x,int y){
    x=find(x),y=find(y);
    if(x!=y){
        if(rank[x]<=rank[y]) fa[x]=y,rank[y]=max(rank[y],rank[x]+1);
        else fa[y]=x,rank[x]=max(rank[x],rank[y]+1);
    }
} 

复杂度:路径压缩+按秩合并: Θ ( α ( n ) ) \Theta(\alpha(n)) ,其他: Θ ( l o g n ) \Theta(logn)

*可持久化并查集

//bzoj 3674
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=200100;
const int maxl=20;
struct data{
    int lson,rson,id,val,_Size;
} tree[maxn+2*maxn*maxl];
int Root[maxn];
int cur;
int w[maxn];
int n,m,lt,nt,lans;
void Build(int x){
    tree[x].id=x;
    tree[x].val=x;
    tree[x]._Size=1;
    int left=x<<1;
    if (left<=n){
        tree[x].lson=left;
        Build(left);
    }
    int right=left|1;
    if (right<=n){
        tree[x].rson=right;
        Build(right);
    }
}
data Query(int root,int L,int x){
    if (x==L) return tree[root];
    if (x&(1<<(w[x]-w[L]-1))) return Query(tree[root].rson,L*2+1,x);
    else return Query(tree[root].lson,L*2,x);
}
data Find_fa(int x){
    data y=Query(Root[lt],1,x);
    while (y.id!=y.val) y=Query(Root[lt],1,y.val);
    return y;
}
void Update(int root,int L,int x,int nid,int v){
    if (L==x){
        if (nid==0) tree[root].val=v;
        else tree[root]._Size=v;
        return;
    }
    if (x&(1<<(w[x]-w[L]-1))){
        data temp=tree[ tree[root].rson ];
        tree[root].rson=++cur;
        tree[cur]=temp;
        Update(cur,L*2+1,x,nid,v);
    }
    else{
        data temp=tree[ tree[root].lson ];
        tree[root].lson=++cur;
        tree[cur]=temp;
        Update(cur,L*2,x,nid,v);
    }
}
void Add(int x,int y){
    data fx=Find_fa(x);
    data fy=Find_fa(y);
    if (fx.val==fy.val){
        Root[nt]=Root[lt];
        return;
    }
    if (fx._Size>fy._Size) swap(fx,fy);
    Root[nt]=++cur;
    tree[cur]=tree[ Root[lt] ];
    Update(cur,1,fx.id,0,fy.id);
    tree[++cur]=tree[ Root[nt] ];
    Root[nt]=cur;
    Update(cur,1,fy.id,1,fx._Size+fy._Size);
}
int main(){
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d%d",&n,&m);
    Build(1);
    Root[1]=1;
    cur=n;
    lt=1,nt=1;
    lans=0;
    w[1]=1;
    for (int i=2; i<maxn; i++) w[i]=w[i/2]+1;
    for (int i=1; i<=m; i++){
        int op;
        scanf("%d",&op);
        if (op==1){
            int a,b;
            scanf("%d%d",&a,&b);
            a^=lans,b^=lans;
            nt++;
            Add(a,b);
            lt=nt;
        }
        if (op==2){
            int k;
            scanf("%d",&k);
            k^=lans;
            lt=k+1;
            nt++;
            Root[nt]=Root[lt];
            lt=nt;
        }
        if (op==3){
            int a,b;
            scanf("%d%d",&a,&b);
            a^=lans,b^=lans;
            data fa=Find_fa(a);
            data fb=Find_fa(b);
            bool f=(fa.val==fb.val);
            printf("%d\n",f);
            lans=f;
            nt++;
            Root[nt]=Root[lt];
            lt=nt;
        }
    }
    return 0;
}

单调队列

【简介】

单调队列,即单调递减或单调递增的队列。

【代码实现】
//https://www.luogu.org/problemnew/show/P1886
#include<cstdio>
int q[1000100],p[1000100],a[1000100];
int head,tail,n,k;
void Max(){
    head=1;tail=0;
    for(int i=1;i<=n;i++){
        while(tail>=head && q[tail]<=a[i]) tail--;
        q[++tail]=a[i];
        p[tail]=i;
        while(i-k+1>p[head]) head++;
        if(i>=k)  printf("%d ",q[head]);
    }
}
void Min(){
    head=1;tail=0;
    for(int i=1;i<=n;i++){
        while(head<=tail && q[tail]>=a[i])  tail--;
        q[++tail]=a[i];
        p[tail]=i;
        while(i-k+1>p[head]) head++;
        if(i>=k)  printf("%d ",q[head]);
    }
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	Min();
	printf("\n");
	Max();
	return 0;
}

复杂度 Θ ( n ) \Theta(n)


单调栈

【简介】

单调递增或单调减的栈,跟单调队列
差不多,但是只用到它的一端,利用它可以用来解决一些 A C M / I C P C ACM/ICPC O I OI 的题目。

【代码实现】
//poj 3250
#include<cstdio>
#include<iostream>
#include<stack>
using namespace std;
int main(){
    int i,n,top,a[80010]; //top指向栈顶元素 
    long long ans; //存储最终结果 
    stack<int> st; //st为栈,每次入栈的是每头牛的位置 
    while(~scanf("%d",&n)){
        while(!st.empty()) st.pop(); //清空栈 
        for(i=0;i<n;i++) scanf("%d",&a[i]);
        a[n]=2147483647; //将最后一个元素设为最大值 
        ans=0;
        for(i=0;i<=n;i++){
            if(st.empty()||a[i]<a[st.top()])st.push(i);
            //如果栈为空或入栈元素小于栈顶元素,则入栈 
            else{
                while(!st.empty()&&a[i]>=a[st.top()]){//如果栈不为空并且栈顶元素不大于入栈元素,则将栈顶元素出栈 
                    top=st.top(); //获取栈顶元素 
                    st.pop();     //栈顶元素出栈 
                    //这时候也就找到了第一个比栈顶元素大的元素 
                    //计算这之间牛的个数,为下标之差-1 
                    ans+=(i-top-1); 
                }
                st.push(i); //当所有破坏栈的单调性的元素都出栈后,将当前元素入栈 
            }
        }
        cout<<ans; 
    }
    return 0;
}

复杂度 Θ ( n ) \Theta(n)


线段树

【简介】

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为 Θ ( l o g n ) \Theta(logn) 。而未优化的空间复杂度为 2 n 2n ,实际应用时一般还要开 4 n 4n 的数组以免越界,因此有时需要离散化让空间压缩。

【代码实现】

朴素线段树

线段树 1 1
//https://www.luogu.org/problemnew/show/P3372
#include<iostream>
#include<cstdio>
#include<ctype.h>
typedef long long ll;
inline ll read(){
    ll x=0;int f=0;char ch=getchar();
    while(!isdigit(ch))f|=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}
ll ans,L,R,w;
struct Tree{
    ll l,r,w,flag;
}tree[262144+7];
void update(int o){
    tree[o].w=tree[o<<1].w+tree[o<<1|1].w;
}
void Down(int o){
    tree[o<<1].flag+=tree[o].flag;
    tree[o<<1|1].flag+=tree[o].flag;
    tree[o<<1].w+=(tree[o<<1].r-tree[o<<1].l+1)*tree[o].flag;
    tree[o<<1|1].w+=(tree[o<<1|1].r-tree[o<<1|1].l+1)*tree[o].flag;
    tree[o].flag=0;
}
void build(int o,int l,int r){
    tree[o].l=l,tree[o].r=r;
    if(l==r){
        tree[o].w=read();
        return ;
    }
    int mid=(l+r)>>1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    update(o);
}
void add_point(int o){
    if(tree[o].l==tree[o].r){
        ans+=tree[o].w;
    return ;
    }
    if(tree[o].flag) Down(o);
    int mid=(tree[o].l+tree[o].r)>>1;
    if(L<=mid) add_point(o<<1);
    else add_point(o<<1|1);
    update(o);
}
void ask_interval(int o){
    if(tree[o].l>=L && tree[o].r<=R){
        ans+=tree[o].w;
        return ; 
    }
    if(tree[o].flag) Down(o);
    int mid=(tree[o].l+tree[o].r)>>1;
    if(L<=mid)ask_interval(o<<1);
    if(R>mid) ask_interval(o<<1|1);
}
void add_interval(int o){
    if(tree[o].l>=L && tree[o].r<=R){
        tree[o].w+=(tree[o].r-tree[o].l+1)*w;
        tree[o].flag+=w;
        return ;
    }
    if(tree[o].flag) Down(o);
    int mid=(tree[o].l+tree[o].r)>>1;
    if(L<=mid) add_interval(o<<1);
    if(R>mid) add_interval(o<<1|1);
    update(o);
}
int main(){
    int n=read(),m=read();
    build(1,1,n);
    for(int i=1;i<=m;++i){
        int ooo=read();L=read(),R=read();
        if(ooo==1){
        w=read();
            add_interval(1);
        }
        else {
            ans=0;ask_interval(1);
            printf("%lld\n",ans);
        }
    }
    return 0;
}
线段树 2 2
//https://www.luogu.org/problemnew/show/P3373
#include<iostream>
#include<cstdio>
#include<ctype.h>
using namespace std;
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f|=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}
int L,R,W,mod;
long long ans;
struct Tree{
    int l,r;
    long long w,add,mul;
}tree[262144+7]; 
inline void update(int o){
    tree[o].w=(tree[o<<1].w+tree[o<<1|1].w)%mod;
}
inline void Down(int o){
    if(tree[o].mul!=1){
        tree[o<<1].w=tree[o<<1].w*tree[o].mul%mod;
        tree[o<<1|1].w=tree[o<<1|1].w*tree[o].mul%mod;
        tree[o<<1].add=tree[o<<1].add*tree[o].mul%mod;
        tree[o<<1|1].add=tree[o<<1|1].add*tree[o].mul%mod;
        tree[o<<1].mul=tree[o<<1].mul*tree[o].mul%mod;
        tree[o<<1|1].mul=tree[o<<1|1].mul*tree[o].mul%mod;
        tree[o].mul=1;
    }
    if(tree[o].add){
        tree[o<<1].w=(tree[o<<1].w+(tree[o<<1].r-tree[o<<1].l+1)*tree[o].add%mod)%mod;
        tree[o<<1|1].w=(tree[o<<1|1].w+(tree[o<<1|1].r-tree[o<<1|1].l+1)*tree[o].add%mod)%mod;
        tree[o<<1].add=(tree[o<<1].add+tree[o].add)%mod;
        tree[o<<1|1].add=(tree[o<<1|1].add+tree[o].add)%mod;
        tree[o].add=0;
    }
}
void ask_interval(int o){
    if(tree[o].l>=L && tree[o].r<=R){
        ans=(ans+tree[o].w)%mod;
        return ;
    }
    Down(o);
    int mid=tree[o].l+tree[o].r>>1;
    if(L<=mid)ask_interval(o<<1);
    if(R>mid)ask_interval(o<<1|1);
}
void add_interval(int o){
    if(tree[o].l>=L && tree[o].r<=R){
        tree[o].w=(tree[o].w+(tree[o].r-tree[o].l+1)*W%mod)%mod;
        tree[o].add+=W;
        return ;
    }
    Down(o);
    int mid=tree[o].l+tree[o].r>>1;
    if(L<=mid)add_interval(o<<1);
    if(R>mid)add_interval(o<<1|1);
    update(o);
}
void mul_interval(int o){
    if(tree[o].l>=L && tree[o].r<=R){
        tree[o].w=tree[o].w*W%mod;
        tree[o].add=tree[o].add*W%mod;
        tree[o].mul=tree[o].mul*W%mod;
        return ;
    }
    Down(o);
    int mid=tree[o].l+tree[o].r>>1;
    if(L<=mid)mul_interval(o<<1);
    if(R>mid)mul_interval(o<<1|1);
    update(o);
}
void build(int o,int l,int r){
    tree[o].mul=1;
    tree[o].l=l,tree[o].r=r;
    if(l==r){
        tree[o].w=read();
        return ;
    }
    int mid=l+r>>1;
    build(o<<1,l,mid),build(o<<1|1,mid+1,r);
    update(o);
}
int main(){
    int n=read(),m=read();mod=read();
    build(1,1,n);
    for(int i=1;i<=m;++i){
        int o=read();L=read(),R=read();
        if(o==3){
            ans=0;ask_interval(1);
            printf("%lld\n",ans);
        }
        else{
            W=read();
            o&1?mul_interval(1):add_interval(1);
        }
    }
    return 0;
}

复杂度 Θ ( ( n + q ) l o g \Theta((n+q)log n ) n)


*可持久化线段树(主席树)

//https://www.luogu.org/problemnew/show/P3834
#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<algorithm>
using namespace std;
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f|=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}
struct Tree{
    int lc,rc,ans;
}tree[3600007];
int rt[200007],a[200007],s[200007],tot;
void insert(int &root,int last,int l,int r,int k){
    root=++tot;
    tree[root].lc=tree[last].lc;
    tree[root].rc=tree[last].rc;
    tree[root].ans=tree[last].ans+1;
    if(l==r) return ;
    int mid=l+r>>1;
    if(k<=mid)insert(tree[root].lc,tree[last].lc,l,mid,k);
    else insert(tree[root].rc,tree[last].rc,mid+1,r,k);
}
int quary(int L,int R,int l,int r,int k){
    if(l==r) return l;
    int mid=l+r>>1,o=tree[tree[R].lc].ans-tree[tree[L].lc].ans;
    if(k<=o) return quary(tree[L].lc,tree[R].lc,l,mid,k);
    else return quary(tree[L].rc,tree[R].rc,mid+1,r,k-o);
}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=n;++i)s[i]=a[i]=read();
    sort(s+1,s+1+n);
    int len=unique(s+1,s+n+1)-s-1;
    for(int i=1;i<=n;++i){
        int o=lower_bound(s+1,s+len+1,a[i])-s;
        insert(rt[i],rt[i-1],1,len,o);
    }
    while(m--){
        int l=read(),r=read(),k=read();
        printf("%d\n",s[quary(rt[l-1],rt[r],1,len,k)]);
    }
    return 0;
}

*权值线段树

//https://www.luogu.org/problemnew/show/P1801
#include<bits/stdc++.h>
#define N 200005
using namespace std;
int m,n,k;
int a[N],b[N],u[N];
struct MM{
    int l,r,s;
}tree[N<<2];
inline void build(int root,int L,int R){
    tree[root].l=L;
    tree[root].r=R;
    if(L==R) return;
    int mid=(L+R)>>1;
    build(root<<1,L,mid);
    build(root<<1|1,mid+1,R);
}
inline void update(int root,int t){
    if(tree[root].l==tree[root].r){
        tree[root].s++;//个数加一
        return;
    }
    int mid=(tree[root].l+tree[root].r)>>1;
    if(t<=mid) update(root<<1,t);
    else update(root<<1|1,t);
    tree[root].s=tree[root<<1].s+tree[root<<1|1].s;
}
inline int query(int root,int t){
    if(tree[root].l==tree[root].r)
        return tree[root].l;
    if(t<=tree[root<<1].s) return query(root<<1,t);
    else return query(root<<1|1,t-tree[root<<1].s);
}
int main(){
    cin>>m>>n;
    for(int i=1;i<=m;i++){
        cin>>a[i];
        b[i]=a[i];
    }
    for(int i=1;i<=n;i++)cin>>u[i];
    sort(b+1,b+m+1);
    int s=unique(b+1,b+m+1)-(b+1);//离散化(如果值域很大的话),s是数组b中不重复的数的个数
    build(1,1,s);//依s建树
    int h=0;
    while(n!=h){
        h++;
        for(int i=u[h-1]+1;i<=u[h];i++){
            int v=lower_bound(b+1,b+s+1,a[i])-b;//v是a[i]在数组b中所处的位置(注意之前数组b排了序)
            update(1,v);
        }
        cout<<b[query(1,++k)]<<endl;
    }
    return 0;
}

*动态开点线段树

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define clr(a,b) memset(a,b,sizeof a)
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int maxn = 2e6+5e5+11;
const int oo = 0x3f3f3f3f;
int T[70];
struct ST{
    int L[maxn<<2],R[maxn<<2],minx[maxn<<2];
    int tot;
    void init(){
        clr(T,0);
        tot=0;
        L[0]=R[0]=0;
        minx[0]=oo;
    }
    void pu(int o){
        minx[o]=min(minx[L[o]],minx[R[o]]); 
    }
    void update(int &o,int l,int r,int y,int x){
        if(!o){
            o=++tot;
            L[o]=R[o]=0;
            minx[o]=x;
        }
        if(l==r){
            minx[o]=min(minx[o],x);
            return ;
        }
        int m=l+r>>1;
        if(y<=m) update(L[o],l,m,y,x);
        else update(R[o],m+1,r,y,x);
        pu(o);
    }
    int query(int &o,int l,int r,int LL,int RR){
        if(!o){
            return oo; //很重要!!!
        }
        if(LL<=l&&r<=RR){
            return minx[o];
        }
        int m=l+r>>1;
        int ans=oo;
        if(LL<=m) ans=min(ans,query(L[o],l,m,LL,RR));
        if(RR>m) ans=min(ans,query(R[o],m+1,r,LL,RR));
        return ans;
    }
}st; 
const int n = 1e6;
int main(){
    int op,x,y,yy1,yy2,c;
    st.init();
    while(scanf("%d",&op)!=EOF){
        if(op==3)break;
        else if(op==0) st.init();
        else if(op==1){
            scanf("%d%d%d",&x,&y,&c);
            st.update(T[c],1,n,y,x);
        }
        else if(op==2){
            scanf("%d%d%d",&x,&yy1,&yy2);
            int ans=0,tmp;
            rep(i,0,50){
                tmp=st.query(T[i],1,n,yy1,yy2);
                if(tmp<=x) ans++;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

树状数组

【简介】

树状数组 ( B i n a r y (Binary I n d e x e d Indexed T r e e Tree , F e n w i c k Fenwick T r e e ) Tree) 是一个查询和修改复杂度都为 Θ ( l o g n ) \Theta(logn) 的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在 Θ ( l o g n ) \Theta(logn) 的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

【代码实现】
#include<iostream>
#include<ctype.h>
#include<cstdio>
#include<algorithm>
#define lowbit(x) x&(-x)
using namespace std;
long long n,q,C[1000007];
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f|=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}
inline void add(int x,int y){
    while(x<=n)C[x]+=y,x+=lowbit(x);
}
inline long long getsum(int x){
    long long ans=0;
    while(x>0) ans+=C[x],x-=lowbit(x);
    return ans;
}
int main(){
    n=read(),q=read();
//    cout<<n<<q;
    for(int i=1;i<=n;++i){
        int x=read();add(i,x);
    }
    while(q--){
        int o=read(),l=read(),r=read();
        if(o&1)add(l,r);
        else printf("%lld\n",getsum(r)-getsum(l-1));
    }
    return 0;
}

复杂度 Θ ( l o g \Theta(log n ) n)

ST表

【简介】

S T ST 表类似树状数组,线段树这两种数据结构,是一种用于解决 R M Q ( R a n g e RMQ(Range M i n i m u m / M a x i m u m Minimum/Maximum Q u e r y Query ,即区间最值查询)问题的离线数据结构。

【代码实现】
#include<iostream>
#include<cstdio> 
#include<cmath>
using namespace std;
int f[100001][40];
int n,m,l,r;
int main(){
    scanf("%d%d",&n,&m);
	int p=log2(n);
    for(int i=1;i<=n;i++) scanf("%d",&f[i][0]);
    for(int j=1;j<=p;j++)
        for (int i=1;i<=n-(1<<j)+1;i++)
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&l,&r);
        p=log2(r-l+1);
        printf("%d\n",max(f[l][p],f[r-(1<<p)+1][p]));
    }
    return 0;
}

复杂度 Θ ( n \Theta(n l o g n + q ) logn+q)


*块状链表

【简介】

其基本定应用为:把一个长度为 n n 的串,分成约块,相邻两块的大小不小于根号 N N ,每一块的大小不超过 2 n 2\sqrt {n} 。这样就可以在的时间内解决一个插入、询问、拆分、合并等等的操作。

【代码实现】
//bzoj 3337
#include<cstring>
#include<iostream>
#include<cctype>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#define writ(x,c) write(x),putchar(c);
typedef long long ll;
const int N=10000,MAX=1<<29;
using namespace std;
inline int read(){
    char c;int x=0;bool f=0;
    for(;!isdigit(c);c=getchar()) if(c=='-') f=1;
    for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    return (f ? -x : x);
}
template <class _T>
void write(_T x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int n,m,size;
struct node{
    int d[1003],s[1003],rev,add,same,size,next;
    ll sum;
}a[N];
queue<int> q;
void Init(){}
int new_node(){
    int temp=q.front();q.pop();
    return temp;
}
void clear(int x){a[x].rev=a[x].add=a[x].same=a[x].size=a[x].rev=0;}
void erase(int x){q.push(x);clear(x);}
void find(int &pos,int &now){
    for(now=0;a[now].next!=-1&&pos>a[now].size;now=a[now].next)
        pos-=a[now].size;
}
void down(int now){
    int tot=a[now].size; 
    if(a[now].rev){
        a[now].rev=false;
        int tt=(tot>>1);
        for(int u=1;u<=tt;u++) swap(a[now].d[u],a[now].d[tot-u+1]);
    }
    if(a[now].same!=0){
        for(int u=1;u<=tot;u++)
            a[now].d[u]=a[now].same;
        a[now].sum=a[now].same*tot,a[now].same=0;
    }
    if(a[now].add!=0){
        for(int u=1;u<=tot;u++)
            a[now].d[u]+=a[now].add;
        a[now].sum=a[now].sum+a[now].add*tot,a[now].add=0;
    }
}
void update(int x){
    a[x].sum=0;
    for(int u=1;u<=a[x].size;u++)
        a[x].sum+=a[x].d[u],a[x].s[u]=a[x].d[u];
    sort(a[x].s+1,a[x].s+a[x].size+1);
}
void spilt(int now,int pos){
    down(now);
    int t=new_node();
    for(int u=pos;u<=a[now].size;u++)
        a[t].d[++a[t].size]=a[now].d[u];
    a[t].next=a[now].next,a[now].next=t,a[now].size=max(pos-1,0);
    update(t);update(now);
}
void Merge(int now){
    int k=a[now].next;
    down(now);down(k);
    for(int u=1;u<=a[k].size;u++)
        a[now].d[++a[now].size]=a[k].d[u];
    a[now].next=a[k].next;erase(k);
    update(now);
}
void maintain(int now){
    for(;now!=-1;now=a[now].next)
        if(a[now].next!=-1&&a[now].size+a[a[now].next].size<=size)
            Merge(now); 
}
void ins(int pos,int x){
    int now;pos++;
    find(pos,now);spilt(now,pos);
    a[now].d[++a[now].size]=x,a[now].sum+=x;
    int lalal;
    for(lalal=1;lalal<a[now].size;lalal++)
        if(a[now].s[lalal]>x) break;
    for(int u=a[now].size;u>lalal;u--)
        a[now].s[u]=a[now].s[u-1];
    a[now].s[lalal]=x;
    maintain(now);
}
void del(int pos){
    int now;
    find(pos,now);down(now);
    for(int u=pos+1;u<=a[now].size;u++)
        a[now].d[u-1]=a[now].d[u];
    a[now].size--;
    update(now);maintain(now);
}
void solve(int l,int r,int &lp,int &rp){
    int pos=l;
    find(pos,lp);spilt(lp,pos);
    pos=r+1;
    find(pos,rp);spilt(rp,pos);
    pos=r;
    find(pos,rp);
}
int st[N];
void rverse(int l,int r){
    int lp,rp;
    solve(l,r,lp,rp);
    int now=lp,top=0;
    for(int u=a[lp].next;u!=a[rp].next;u=a[u].next)
        st[++top]=u,a[u].rev^=1;
    a[st[1]].next=a[rp].next;
    for(int u=top;u>1;u--)
        a[st[u]].next=st[u-1];
    a[lp].next=rp;
    maintain(lp);
}
void slip(int l,int r,int k){
    int lp,mp,rp,np;
    solve(l,r-k,lp,mp),solve(r-k+1,r,mp,rp);
    np=a[lp].next,a[lp].next=a[mp].next,a[mp].next=a[rp].next,a[rp].next=np;
    maintain(lp); 
}
void add(int l,int r,int val){
    int lp,rp;
    solve(l,r,lp,rp);
    for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
        a[now].add+=val,a[now].sum=a[now].sum+a[now].size*val;
    maintain(lp);
}
void same(int l,int r,int val){
    int lp,rp;
    solve(l,r,lp,rp);
    for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
        a[now].add=0,a[now].same=val,a[now].sum=a[now].size*val;
    maintain(lp);
}
ll getsum(int l,int r){
    int lp,rp;
    solve(l,r,lp,rp);
    ll ans=0;
    for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
        ans=ans+a[now].sum;
    maintain(lp);
    return ans;
}
int getcha(int l,int r){
    int lp,rp;
    solve(l,r,lp,rp);
    int maxx=-MAX,minn=MAX;
    for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
        if(a[now].size!=0)
            if(a[now].same!=0)
                minn=min(minn,a[now].same+a[now].add),
                maxx=max(maxx,a[now].same+a[now].add);
            else
                minn=min(minn,a[now].s[1]+a[now].add),
                maxx=max(maxx,a[now].s[a[now].size]+a[now].add);
    maintain(lp);
    return maxx-minn;
}
int near(int l,int r,int val){
    int lp,rp;
    solve(l,r,lp,rp);
    int ans=MAX;
    for(int now=a[lp].next;now!=a[rp].next;now=a[now].next){
        if(a[now].same)
            ans=min(ans,abs(val-a[now].same-a[now].add));
        else {
            int id=lower_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add)-a[now].s;
            if(id!=a[now].size+1)
                ans=min(ans,a[now].s[id]+a[now].add-val);
            if(id!=1)
                id--,ans=min(ans,val-a[now].s[id]-a[now].add);
        }
    }
    maintain(lp);
    return ans;
}
int rank(int l,int r,int k){
    int lp,rp;
    solve(l,r,lp,rp);
    int ll=0,rr=MAX;
    while(ll<rr){
        int mid=(ll+rr)/2+1,sum=1;
        for(int now=a[lp].next;now!=a[rp].next;now=a[now].next){
            if(a[now].same!=0){
                if(a[now].same+a[now].add<mid)
                    sum=sum+a[now].size;
            }
            else{
                int id=upper_bound(a[now].s+1,a[now].s+a[now].size+1,mid-a[now].add-1)-a[now].s;  
                sum=sum+max(0,id-1);
            }
        }
        if(k>=sum) ll=mid;
        else rr=mid-1;
    }
    maintain(lp);  
    return ll;
}
int sec(int l,int r,int val){
    int lp,rp;
    solve(l,r,lp,rp);
    int ans=0;
    for(int now=a[lp].next;now!=a[rp].next;now=a[now].next){
        if(a[now].same!=0){
            if(a[now].same+a[now].add<val)
                ans=ans+a[now].size;
        }
        else{
            int it=upper_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add-1)-a[now].s;
            ans=ans+it-1;
        }
    }
    maintain(lp);
    return ans;
}
int main(){
    n=read(),size=sqrt(n);
    for(int i=1;i<N;i++) q.push(i);a[0].next=-1;a[0].size=0;
    for(int u=1;u<=n;u++){
        int x=read();ins(u-1,x); 
    }
    m=read();
    while(m--){
        register int op,x,y,z;op=read();
        switch(op){
            case 1:x=read();y=read();ins(x,y);break;
            case 2:x=read();del(x);break;
            case 3:x=read();y=read();rverse(x,y);break;
            case 4:x=read();y=read();z=read();slip(x,y,z);break;
            case 5:x=read();y=read();z=read();add(x,y,z);break;
            case 6:x=read();y=read();z=read();same(x,y,z);break;
            case 7:x=read();y=read();writ(getsum(x,y),'\n');break;
            case 8:x=read();y=read();writ(getcha(x,y),'\n');break;
            case 9:x=read();y=read();z=read();writ(near(x,y,z),'\n');break;
            case 10:x=read();y=read();z=read();writ(rank(x,y,z),'\n');break;
            case 11:x=read();y=read();z=read();writ(sec(x,y,z),'\n');break;
        }
    }
    return 0;
}
/*这代码简直恶心死了*/

复杂度 Θ ( ( n + q ) n ) \Theta((n+q)\sqrt{n})

*Splay(伸展树)

【简介】

伸展树 ( S p l a y (Splay T r e e ) Tree) ,也叫分裂树,是一种二叉排序树,它能在 Θ ( l o g Θ(log n ) n) 内完成插入、查找和删除操作。它由 D a n i e l Daniel S l e a t o r Sleator R o b e r t Robert T a r j a n Tarjan 创造,后勃刚对其进行了改进。它的优势在于不需要记录用于平衡树的冗余信息。在伸展树上的一般操作都基于伸展操作。

【代码实现】
//https://www.luogu.org/problemnew/show/P3391
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m; 
int fa[N],ch[N][2],size[N],rev[N],rt;
inline void pushup(int x){
    size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void pushdown(int x){
    if(rev[x]){
        swap(ch[x][0],ch[x][1]);
        rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;rev[x]=0;
    }
}
void rotate(int x,int &k){
    int y=fa[x],z=fa[y],kind;
    if(ch[y][0]==x)kind=1;else kind=0;
    if(y==k)k=x;
    else{if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;}
    ch[y][kind^1]=ch[x][kind];fa[ch[y][kind^1]]=y;
    ch[x][kind]=y;fa[y]=x;fa[x]=z;
    pushup(x);pushup(y);
}
void splay(int x,int &k){
    while(x!=k){
        int y=fa[x],z=fa[y];
        if(y!=k){
            if((ch[y][0]==x)^(ch[z][0]==y))rotate(x,k);
            else rotate(y,k);
        }
        rotate(x,k);
    }
}
void build(int l,int r,int f){
    if(l>r)return;
    int mid=(l+r)/2;
    if(mid<f)ch[f][0]=mid;else ch[f][1]=mid;
    fa[mid]=f;size[mid]=1;
    if(l==r)return;
    build(l,mid-1,mid);build(mid+1,r,mid);
    pushup(mid);
}
int find(int x,int k){
    pushdown(x);int s=size[ch[x][0]];
    if(k==s+1)return x;
    if(k<=s)return find(ch[x][0],k);
    else return find(ch[x][1],k-s-1);
}
void rever(int l,int r){
    int x=find(rt,l),y=find(rt,r+2);
    splay(x,rt);splay(y,ch[x][1]);int z=ch[y][0];
    rev[z]^=1;
}
int main(){
    scanf("%d%d",&n,&m);
    rt=(n+3)/2;build(1,n+2,rt);
    for(int i=1;i<=m;i++){
        int L,R;scanf("%d%d",&L,&R);
        rever(L,R);
    }
    for(int i=2;i<=n+1;i++)printf("%d ",find(rt,i)-1);
    return 0;
}

复杂度 Θ ( n \Theta(n l o g n ) logn)


*Treap

【简介】

树堆,在数据结构中也称 T r e a p Treap ,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为 Θ ( l o g \Theta(log n ) n)
相对于其他的平衡二叉搜索树, T r e a p Treap 的特点是实现简单 (这道题一点都不简单) ,且能基本实现随机平衡的结构。

【代码实现】
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
typedef long long LL;
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
}
//第一次打treap,不压行写注释XD
const int maxn = 1000019,INF = 1e9;
//平衡树,利用BST性质查询和修改,利用随机和堆优先级来保持平衡,把树的深度控制在log N,保证了操作效率
//基本平衡树有以下几个比较重要的函数:新建,插入,删除,旋转
//节点的基本属性有val(值),dat(随机出来的优先级)
//通过增加属性,结合BST的性质可以达到一些效果,如size(子树大小,查询排名),cnt(每个节点包含的副本数)等
int na;
int ch[maxn][2];//[i][0]代表i左儿子,[i][1]代表i右儿子
int val[maxn],dat[maxn];
int size[maxn],cnt[maxn];
int tot,root;
int New(int v){//新增节点,
    val[++tot] = v;//节点赋值
    dat[tot] = rand();//随机优先级
    size[tot] = 1;//目前是新建叶子节点,所以子树大小为1
    cnt[tot] = 1;//新建节点同理副本数为1
    return tot;
}
void pushup(int id){//和线段树的pushup更新一样
    size[id] = size[ch[id][0]] + size[ch[id][1]] + cnt[id];//本节点子树大小 = 左儿子子树大小 + 右儿子子树大小 + 本节点副本数
}
void build(){
    root = New(-INF),ch[root][1] = New(INF);//先加入正无穷和负无穷,便于之后操作(貌似不加也行)
    pushup(root);//因为INF > -INF,所以是右子树,
}
void Rotate(int &id,int d){//id是引用传递,d(irection)为旋转方向,0为左旋,1为右旋
    int temp = ch[id][d ^ 1];//旋转理解:找个动图看一看就好(或参见其他OIer的blog)
    ch[id][d ^ 1] = ch[temp][d];//这里讲一个记忆技巧,这些数据都是被记录后马上修改
    ch[temp][d] = id;//所以像“Z”一样
    id = temp;//比如这个id,在上一行才被记录过,ch[temp][d]、ch[id][d ^ 1]也是一样的
    pushup(ch[id][d]),pushup(id);//旋转以后size会改变,看图就会发现只更新自己和转上来的点,pushup一下,注意先子节点再父节点
}//旋转实质是({在满足BST的性质的基础上比较优先级}通过交换本节点和其某个叶子节点)把链叉开成二叉形状(从而控制深度),可以看图理解一下
void insert(int &id,int v){//id依然是引用,在新建节点时可以体现
    if(!id){
        id = New(v);//若节点为空,则新建一个节点
        return ;
        }
    if(v == val[id])cnt[id]++;//若节点已存在,则副本数++;
    else{//要满足BST性质,小于插到左边,大于插到右边
        int d = v < val[id] ? 0 : 1;//这个d是方向的意思,按照BST的性质,小于本节点则向左,大于向右
        insert(ch[id][d],v);//递归实现
        if(dat[id] < dat[ch[id][d]])Rotate(id,d ^ 1);//(参考一下图)与左节点交换右旋,与右节点交换左旋
        }
    pushup(id);//现在更新一下本节点的信息
}
void Remove(int &id,int v){//最难de部分了
    if(!id)return ;//到这了发现查不到这个节点,该点不存在,直接返回
    if(v == val[id]){//检索到了这个值
        if(cnt[id] > 1){cnt[id]--,pushup(id);return ;}//若副本不止一个,减去一个就好
        if(ch[id][0] || ch[id][1]){//发现只有一个值,且有儿子节点,我们只能把值旋转到底部删除
            if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]]){//当前点被移走之后,会有一个新的点补上来(左儿子或右儿子),按照优先级,优先级大的补上来
                Rotate(id,1),Remove(ch[id][1],v);//我们会发现,右旋是与左儿子交换,当前点变成右节点;左旋则是与右儿子交换,当前点变为左节点
                }
            else Rotate(id,0),Remove(ch[id][0],v);
            pushup(id);
            }
        else id = 0;//发现本节点是叶子节点,直接删除
        return ;//这个return对应的是检索到值de所有情况
        }
    v < val[id] ? Remove(ch[id][0],v) : Remove(ch[id][1],v);//继续BST性质
    pushup(id);
}
int get_rank(int id,int v){
    if(!id)return 0;//若查询值不存在,返回
    if(v == val[id])return size[ch[id][0]] + 1;//查询到该值,由BST性质可知:该点左边值都比该点的值(查询值)小,故rank为左儿子大小 + 1
    else if(v < val[id])return get_rank(ch[id][0],v);//发现需查询的点在该点左边,往左边递归查询
    else return size[ch[id][0]] + cnt[id] + get_rank(ch[id][1],v);//若查询值大于该点值。说明询问点在当前点的右侧,且此点的值都小于查询值,所以要加上cnt[id]
}
int get_val(int id,int rank){
    if(!id)return INF;//一直向右找找不到,说明是正无穷
    if(rank <= size[ch[id][0]])return get_val(ch[id][0],rank);//左边排名已经大于rank了,说明rank对应的值在左儿子那里
        else if(rank <= size[ch[id][0]] + cnt[id])return val[id];//上一步排除了在左区间的情况,若是rank在左与中(目前节点)中,则直接返回目前节点(中区间)的值
    else return get_val(ch[id][1],rank - size[ch[id][0]] - cnt[id]);//剩下只能在右区间找了,rank减去左区间大小和中区间,继续递归
}
int get_pre(int v){
    int id = root,pre;//递归不好返回,以循环求解
    while(id){//查到节点不存在为止
        if(val[id] < v)pre = val[id],id = ch[id][1];//满足当前节点比目标小,往当前节点的右侧寻找最优值
        else id = ch[id][0];//无论是比目标节点大还是等于目标节点,都不满足前驱条件,应往更小处靠近
        }
    return pre;
}
int get_next(int v){
    int id = root,next;
    while(id){
        if(val[id] > v)next = val[id],id = ch[id][0];//同理,满足条件向左寻找更小解(也就是最优解)
        else id = ch[id][1];//与上方同理
        }
    return next;
}
int main(){
    build();//不要忘记初始化[运行build()会连同root一并初始化,所以很重要]
    na = RD();
    for(int i = 1;i <= na;i++){
        int cmd = RD(),x = RD();
        if(cmd == 1)insert(root,x);//函数都写好了,注意:需要递归的函数都从根开始,不需要递归的函数直接查询
        else if(cmd == 2)Remove(root,x);
        else if(cmd == 3)printf("%d\n",get_rank(root,x) - 1);//注意:因为初始化时插入了INF和-INF,所以查询排名时要减1(-INF不是第一小,是“第零小”)
            else if(cmd == 4)printf("%d\n",get_val(root,x + 1));//同理,用排名查询值得时候要查x + 1名,因为第一名(其实不是)是-INF
        else if(cmd == 5)printf("%d\n",get_pre(x));
        else if(cmd == 6)printf("%d\n",get_next(x));
        }
    return 0;
}

复杂度 Θ ( n \Theta(n l o g n ) logn)


*范浩强treap

【代码实现】
//https://www.luogu.org/problemnew/show/P3369
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{
    int siz,val,key,lch,rch;
}t[100005];
int tot,seed=233,root=1;
int Rand(){//随机key值
    return seed=int(seed*482711ll%2147483647);
}
int NEW(int val){//新建节点
    t[++tot].siz=1;
    t[tot].val=val;
    t[tot].key=Rand();
    t[tot].lch=t[tot].rch=0;
    return tot; 
}
void update(int now){//维护子树大小
    t[now].siz=t[t[now].lch].siz+t[t[now].rch].siz+1;
}
void split(int now,int &a,int &b,int val){//拆分操作
    //now原Treap,a左树,b右树,val判定值
    //注意传地址符
    if(now==0){
        a=b=0;//若now=0分割完毕;
        return;
    }
    if(t[now].val<=val)//因为now左子树中的所有值都小于now的值,所以若now属于左树,那么他们都属于左树递归now的右子树;
        a=now,split(t[now].rch,t[a].rch,b,val);//a=now已经使a的右子树=now的右子树,不再递归a的右子树;
        else//同上now右子树也都属于左树,递归左子树;
        b=now,split(t[now].lch,a,t[b].lch,val); 
    update(now);//因为后面会用到左(右)树的siz所以更新维护
}
void merge(int &now,int a,int b){//合并操作
    //now新树
    if(a==0||b==0){
        now=a+b;//若某个树已空,则将另一个树整体插入
        return;
    }
    //按照key值合并(堆性质)
    if(t[a].key<t[b].key)//若a树key值<b树,那么b树属于a树的后代,因为b树恒大于a树,那么b树一定属于a树的右后代,a的左子树不变,直接赋给now,递归合并a的右子树和b
        now=a,merge(t[now].rch,t[a].rch,b);
    else
        now=b,merge(t[now].lch,a,t[b].lch);//同理,a树一定是b树的左儿子,递归合并b的左儿子和a
    update(now);
} 
void find(int now,int rank){//找第k大
    while(t[t[now].lch].siz+1!=rank){
        if(t[t[now].lch].siz>=rank)
            now=t[now].lch;//若左子树大小大于rank,找左子树
        else
            rank-=(t[t[now].lch].siz+1),now=t[now].rch;//找右子树(rank-左子树大小-树根(大小为1))号的元素
    }
    printf("%d\n",t[now].val);
}
void insert(int val){
    int x=0,y=0,z;
    z=NEW(val);//新建节点z,作为z树
    split(root,x,y,val);//将树分为两部分,x树为<=待插入值,y树大于
    merge(x,x,z);//合并x树和新节点z(树),赋给x树
    merge(root,x,y);//合并新x树和y树,赋给根
    //就这么简单
}
void delet(int val){
    int x=0,y=0,z=0;
    split(root,x,y,val);//分为x树为<=待删除,y树大于
    split(x,x,z,val-1);//x树分为新x树<待删除,z树等于待删除
    merge(z,t[z].lch,t[z].rch);//合并z树左右儿子,赋给z树,即丢弃z树根节点(实现删除)
    merge(x,x,z);
    merge(root,x,y);//合并,不在重复
}
void get_rank(int val){
    int x=0,y=0;
    split(root,x,y,val-1);//分为小于待查找值的x树和大于等于的y树
    printf("%d\n",t[x].siz+1);//即为待查找值的编号
    merge(root,x,y);//合并
} 
void get_val(int rank){
    find(root,rank);//find查找即可
}
void get_pre(int val){
    int x=0,y=0;
    split(root,x,y,val-1);//x树为<=val-1值即小于val值
    find(x,t[x].siz);//在小于val值中找最大的(编号为siz的)就是前驱
    merge(root,x,y);
}
void get_nxt(int val){
    int x=0,y=0;
    split(root,x,y,val);//x树小于等于val值,那么y树大于val值
    find(y,1);//在y树中找最小的,即为后继
    merge(root,x,y);//合并
}
int main(){
    int i,j,k,m;
    NEW(2147483627);//初始虚节点
    t[1].siz=0;//siz为0,不算虚节点的大小
    scanf("%d",&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&j,&k);
        if(j==1)insert(k);
        if(j==2)delet(k);
        if(j==3)get_rank(k);
        if(j==4)get_val(k);
        if(j==5)get_pre(k);
        if(j==6)get_nxt(k);
    }
    return 0;
}

复杂度 Θ ( n \Theta(n l o g n ) logn)


*替罪羊树

【简介】
替罪羊树是计算机科学中,一种基于部分重建的自平衡二叉搜索树。在替罪羊树上,插入或删除节点的平摊最坏时间复杂度是 Θ ( l o g \Theta(log n ) n) ,搜索节点的最坏时间复杂度是 Θ ( l o g \Theta(log n ) n)

【代码实现】
//https://www.luogu.org/problemnew/show/P3369
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define alpha 0.8
#define maxn 2000001
#define ri register
#define il inline
using namespace std;
struct scapegoat{
    int son[2], val, valid, total;
    bool exist;
}e[maxn];
int memory[maxn]; 
int cur[maxn];
int root, pool, poi, cnt, to_rebuild;
il bool isbad(int now){
    if((double)e[now].valid*alpha <= (double)max(e[e[now].son[0]].valid, e[e[now].son[1]].valid)) return true;
    return false;
}
void dfs(int now){
    if(!now) return;
    dfs(e[now].son[0]);
    if(e[now].exist) cur[++poi] = now;
    else memory[++pool] = now;
    dfs(e[now].son[1]);
}
void build(int l, int r, int &now){
    int mid = l+r>>1; 
    now = cur[mid]; 
    if(l == r){
        e[now].son[0] = e[now].son[1] = 0;
        e[now].total = e[now].valid = 1;
        return; 
    }
    if(l < mid) build(l,mid-1,e[now].son[0]);
    else e[now].son[0] = 0;
    build(mid+1,r,e[now].son[1]); 
    e[now].total = e[e[now].son[0]].total + e[e[now].son[1]].total + 1; 
    e[now].valid = e[e[now].son[0]].valid + e[e[now].son[1]].valid + 1;
}
il void rebuild(int &now){
    poi = 0;
    dfs(now);
    if(poi) build(1,poi,now);
    else now = 0;
}
il int find_rank(int k) {
    int now = root;
    int ans = 1;
    while(now){
        if(e[now].val >= k) now = e[now].son[0];
        else{
            ans += e[e[now].son[0]].valid + e[now].exist;
            now = e[now].son[1];
        }
    }
    return ans;
}
il int find_kth(int k){
    int now = root;
    while(now){
        if(e[now].exist&&e[e[now].son[0]].valid+1 == k) return e[now].val;
        else if(e[e[now].son[0]].valid >= k) now = e[now].son[0];
        else{
            k -= e[e[now].son[0]].valid + e[now].exist;
            now = e[now].son[1];
        }
    }
}
void insert(int &now, int val){
    if(!now){
        now = memory[pool--]; e[now].val = val;
        e[now].exist = e[now].total = e[now].valid = 1;
        e[now].son[0] = e[now].son[1] = 0;
        return;
    }
    e[now].total++, e[now].valid++;
    if(e[now].val >= val) insert(e[now].son[0], val); 
    else insert(e[now].son[1], val);
    if(!isbad(now)){
        if(to_rebuild){
            if(e[now].son[0] == to_rebuild) rebuild(e[now].son[0]);
            else rebuild(e[now].son[1]);
            to_rebuild = 0;
        }
    }
    else to_rebuild = now;
}
il void delete_pos(int &now, int tar){//target(目标)
    if(e[now].exist&&e[e[now].son[0]].valid+ 1 == tar){
        e[now].exist = 0; e[now].valid--; return;
    }
    e[now].valid--;
    if(e[e[now].son[0]].valid + e[now].exist >= tar) delete_pos(e[now].son[0], tar);
    else delete_pos(e[now].son[1],tar-e[e[now].son[0]].valid-e[now].exist);
}
il void delete_val(int tar){
    delete_pos(root, find_rank(tar));
    if((double)e[root].total*alpha > e[root].valid) rebuild(root);
}
int main(){
    int opt, x, m;
    for(int i = 2000000; i >= 1; i--) memory[++pool] = i;
    scanf("%d",&m);
    while(m--){
        scanf("%d%d",&opt,&x);
        if(opt == 1) {insert(root, x);}
        if(opt == 2) {delete_val(x);}
        if(opt == 3) {printf("%d\n",find_rank(x));}
        if(opt == 4) {printf("%d\n",find_kth(x));}
        if(opt == 5) {printf("%d\n",find_kth(find_rank(x)-1));}
        if(opt == 6) {printf("%d\n",find_kth(find_rank(x+1)));}
    }
    return 0;
}

复杂度 Θ ( n \Theta(n l o g log n ) n)


*树套树

【代码实现】
#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <complex>
#include <string>
#include <cstdio>
#include <vector>
#include <bitset>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int MAXN = 1e5 + 100;
int n, m;
int a[MAXN];
namespace Treap{
    struct balanced{
        int w,sz,num,fix;
        int ch[2];
    };
    int tot;
    balanced tree[MAXN * 20];
    int newnode(int w){
        ++tot;
        tree[tot].w = w;
        tree[tot].fix = rand();
        tree[tot].num = 1;
        tree[tot].ch[0] = tree[tot].ch[1] = 0;
        tree[tot].sz = 1;
        return tot;
    }
    void pushup(int p){
        tree[p].sz = tree[tree[p].ch[0]].sz + tree[tree[p].ch[1]].sz + tree[p].num;
    }
    void rotate(int &p, int d){
        int y = tree[p].ch[d];
        tree[p].ch[d] = tree[y].ch[d ^ 1];
        tree[y].ch[d ^ 1] = p;
        pushup(p);
        pushup(y);
        p = y;
    }
    void insert(int &p, int w){
        if (!p)
            p = newnode(w);
        else if (tree[p].w == w)
            ++tree[p].num;
        else {
            if (tree[p].w > w){
                insert(tree[p].ch[0], w);
                if (tree[tree[p].ch[0]].fix > tree[p].fix)
                    rotate(p, 0);
            }
            else{
                insert(tree[p].ch[1], w);
                if (tree[tree[p].ch[1]].fix > tree[p].fix)
                    rotate(p, 1);
            }
        }
        pushup(p);
    }
    void remove(int &p, int w){
        if (tree[p].w > w)
            remove(tree[p].ch[0], w);
        else if (tree[p].w < w)
            remove(tree[p].ch[1], w);
        else{
            if (tree[p].num > 1)
                --tree[p].num;
            else{
                if (!tree[p].ch[0] && !tree[p].ch[1])
                    p = 0;
                else if (!tree[p].ch[0]){
                    rotate(p, 1);
                    remove(tree[p].ch[0], w);
                }
                else if (!tree[p].ch[1]){
                    rotate(p, 0);
                    remove(tree[p].ch[1], w);
                }
                else{
                    if (tree[tree[p].ch[0]].fix > tree[tree[p].ch[1]].fix){
                        rotate(p, 0);
                        remove(tree[p].ch[1], w);
                    }
                    else{
                        rotate(p, 1);
                        remove(tree[p].ch[0], w);
                    }
                }
            }
        }
        if (p)pushup(p);
    }
    int queryrank(int p, int k) // return the highest rank of value 'k'{
        if (!p)return 0;
        if (tree[p].w > k)return queryrank(tree[p].ch[0], k);
        else if (tree[p].w == k)return tree[tree[p].ch[0]].sz;
        else return tree[tree[p].ch[0]].sz + tree[p].num + queryrank(tree[p].ch[1], k);
    }
    int querynum(int p, int k) // return the value of kth rank node{
        if (tree[tree[p].ch[0]].sz + 1 == k)return tree[p].w;
        else if (tree[tree[p].ch[0]].sz + 1 < k)return querynum(tree[p].ch[1], k - 1 - tree[tree[p].ch[0]].sz);
        else return querynum(tree[p].ch[0], k);
    }
    int querypre(int p, int k) // return the prefix of value k{
        if (!p) return -2147483647;
        if (tree[p].w >= k) return querypre(tree[p].ch[0], k);
        else return max(tree[p].w, querypre(tree[p].ch[1], k));
    }
    int querysuf(int p, int k) // return the suffix of value k{
        if (!p)return 2147483647;
        if (tree[p].w <= k)return querysuf(tree[p].ch[1], k);
        else return min(tree[p].w, querysuf(tree[p].ch[0], k));
    }
    void listall(int p){
        if (tree[p].ch[0])listall(tree[p].ch[0]);
        cerr << tree[p].w << ",sz=" << tree[p].num << "   ";
        if (tree[p].ch[1])listall(tree[p].ch[1]);
    }
}
using Treap::listall;
namespace SEG{
    struct segment{
        int l,r;
        int root;
    };
    segment tree[MAXN * 8];
    void build(int p, int l, int r){
        tree[p].l = l;
        tree[p].r = r;
        for (int i = l; i < r + 1; ++i)
            Treap::insert(tree[p].root, a[i]);
        if (l != r){
            int mid = (l + r) / 2;
            build(p * 2, l, mid);
            build(p * 2 + 1, mid + 1, r);
        }
    }
    void modify(int p, int x, int y){
        Treap::remove(tree[p].root, a[x]);
        Treap::insert(tree[p].root, y);
        if (tree[p].l == tree[p].r)return;
        int mid = (tree[p].l + tree[p].r) / 2;
        if (x > mid)modify(p * 2 + 1, x, y);
        else modify(p * 2, x, y);
    }
    int queryrank(int p, int l, int r, int k){ // query the highest rank of value 'k'
        if (tree[p].l > r || tree[p].r < l)return 0;
        if (tree[p].l >= l && tree[p].r <= r)
            return Treap::queryrank(tree[p].root, k);
        else return queryrank(p * 2, l, r, k) + queryrank(p * 2 + 1, l, r, k);
    }
    int querynum(int u, int v, int k){ // query the value of kth num
        int l = 0, r = 1e8;
        while (l < r){
            int mid = (l + r + 1) / 2;
            if (queryrank(1, u, v, mid) < k)l = mid;
            else r = mid - 1;
        }
        return r;
    }
    int querypre(int p, int l, int r, int k){
        if (tree[p].l > r || tree[p].r < l)
            return -2147483647;
        if (tree[p].l >= l && tree[p].r <= r)
            return Treap::querypre(tree[p].root, k);
        else return max(querypre(p * 2, l, r, k), querypre(p * 2 + 1, l, r, k));
    }
    int querysuf(int p, int l, int r, int k){
        if (tree[p].l > r || tree[p].r < l)
            return 2147483647;
        if (tree[p].l >= l && tree[p].r <= r)
            return Treap::querysuf(tree[p].root, k);
        else return min(querysuf(p * 2, l, r, k), querysuf(p * 2 + 1, l, r, k));
    }
}
inline int read(){
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f|=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?-x:x;
}
int main(){
    n = read();
    m = read();
    for (int i = 1; i < n + 1; ++i)a[i] = read();
    SEG::build(1, 1, n);
    for (int i = 0; i < m; ++i){
        int opt = read();
        if (opt == 3){
            int x = read(), y = read();
            SEG::modify(1, x, y);
            a[x] = y;
        }
        else{
            int l = read(), r = read(), k = read();
            if (opt == 1) printf("%d\n", SEG::queryrank(1, l, r, k) + 1);
            else if (opt == 2) printf("%d\n", SEG::querynum(l, r, k));
            else if (opt == 4) printf("%d\n", SEG::querypre(1, l, r, k));
            else printf("%d\n", SEG::querysuf(1, l, r, k));
        }
    }
    return 0;
}

*主席树

好像写过了 . . . . . . ......
不管 . . . ... 换个名字再来一遍

【代码实现】
//https://www.luogu.org/problemnew/show/P3834
#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<algorithm>
using namespace std;
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch))f|=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
    return f?-x:x;
}
struct Tree{
    int lc,rc,ans;
}tree[3600007];
int rt[200007],a[200007],s[200007],tot;
void insert(int &root,int last,int l,int r,int k){
    root=++tot;
    tree[root].lc=tree[last].lc;
    tree[root].rc=tree[last].rc;
    tree[root].ans=tree[last].ans+1;
    if(l==r) return ;
    int mid=l+r>>1;
    if(k<=mid)insert(tree[root].lc,tree[last].lc,l,mid,k);
    else insert(tree[root].rc,tree[last].rc,mid+1,r,k);
}
int quary(int L,int R,int l,int r,int k){
    if(l==r) return l;
    int mid=l+r>>1,o=tree[tree[R].lc].ans-tree[tree[L].lc].ans;
    if(k<=o) return quary(tree[L].lc,tree[R].lc,l,mid,k);
    else return quary(tree[L].rc,tree[R].rc,mid+1,r,k-o);
}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=n;++i)s[i]=a[i]=read();
    sort(s+1,s+1+n);
    int len=unique(s+1,s+n+1)-s-1;
    for(int i=1;i<=n;++i){
        int o=lower_bound(s+1,s+len+1,a[i])-s;
        insert(rt[i],rt[i-1],1,len,o);
    }
    while(m--){
        int l=read(),r=read(),k=read();
        printf("%d\n",s[quary(rt[l-1],rt[r],1,len,k)]);
    }
    return 0;
}

*LCT(Link-Cut Tree)

【简介】

L C T LCT 是一种解决动态树问题的方法,由 t a r j a n tarjan (为什么又是他) 1982 1982 年提出,最原始的论文在这里,在论文中, t a r j a n tarjan 除了介绍了均摊复杂度为 Θ ( l o g 2 \Theta(log_{2} n ) n) 的LCT之外,还介绍了一种时间复杂度为严格 Θ ( l o g 2 \Theta(log_{2} n ) n) 的算法。

【代码实现】
//https://www.luogu.org/problemnew/show/P3690
#include<bits/stdc++.h>
#define N 300005
using namespace std;
int n,m,val[N];
struct Link_Cut_Tree{
    int top,c[N][2],fa[N],xr[N],q[N],rev[N];
    inline void pushup(int x){
        xr[x]=xr[c[x][0]]^xr[c[x][1]]^val[x];
    }
    inline void pushdown(int x){
        int l=c[x][0],r=c[x][1];
        if(rev[x]){
            rev[l]^=1;
            rev[r]^=1;
            rev[x]^=1;
            swap(c[x][0],c[x][1]);
        }
    }
    inline bool isroot(int x){
        return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
    }
    void rotate(int x){
        int y=fa[x],z=fa[y],l,r;
        if(c[y][0]==x)l=0;
        else l=1;
        r=l^1;
        if(!isroot(y)){
            if(c[z][0]==y)c[z][0]=x;
            else c[z][1]=x;
        }
        fa[x]=z;
        fa[y]=x;
        fa[c[x][r]]=y;
        c[y][l]=c[x][r];
        c[x][r]=y;
        pushup(y);
        pushup(x);
    }
    void splay(int x){
        top=1;
        q[top]=x;
        for(int i=x; !isroot(i); i=fa[i])q[++top]=fa[i];
        for(int i=top; i; i--)pushdown(q[i]);
        while(!isroot(x)){
            int y=fa[x],z=fa[y];
            if(!isroot(y)){
                if((c[y][0]==x)^(c[z][0]==y))rotate(x);
                else rotate(y);
            }
            rotate(x);
        }
    }
    void access(int x){
        for(int t=0; x; t=x,x=fa[x])splay(x),c[x][1]=t,pushup(x);
    }
    void makeroot(int x){
        access(x);
        splay(x);
        rev[x]^=1;
    }
    int find(int x){
        access(x);
        splay(x);
        while(c[x][0])x=c[x][0];
        return x;
    }
    void split(int x,int y){
        makeroot(x);
        access(y);
        splay(y);
    }
    void cut(int x,int y){
        split(x,y);
        if(c[y][0]==x)c[y][0]=0,fa[x]=0;
    }
    void link(int x,int y){
        makeroot(x);
        fa[x]=y;
    }
} T;
inline int read(){
    int f=1,x=0;
    char ch;
    do{
        ch=getchar();
        if(ch=='-')f=-1;
    }
    while(ch<'0'||ch>'9');
    do{
        x=x*10+ch-'0';
        ch=getchar();
    }
    while(ch>='0'&&ch<='9');
    return f*x;
}
int main(){
    n=read();
    m=read();
    for(int i=1; i<=n; i++)val[i]=read(),T.xr[i]=val[i];
    while(m--){
        int opt=read();
        if(opt==0){
            int x=read(),y=read();
            T.split(x,y);
            printf("%d\n",T.xr[y]);
        }
        else if(opt==1){
            int x=read(),y=read(),xx=T.find(x),yy=T.find(y);
            if(xx!=yy)T.link(x,y);
        }
        esle if(opt==2){
            int x=read(),y=read(),xx=T.find(x),yy=T.find(y);
            if(xx==yy)T.cut(x,y);
        }
        else if(opt==3){
            int x=read(),y=read();
            T.access(x);
            T.splay(x);
            val[x]=y;
            T.pushup(x);
        }
    }
    return 0;
}

复杂度 Θ ( n \Theta(n l o g n ) logn)


猜你喜欢

转载自blog.csdn.net/weixin_44023181/article/details/85008789