Java教程

文艺平衡树

本文主要是介绍文艺平衡树,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

# 【模板】文艺平衡树

## 题目描述

您需要写一种数据结构(可参考题目标题),来维护一个有序数列。

其中需要提供以下操作:翻转一个区间,例如原有序序列是 5 4 3 2 1,翻转区间是 [2,4] 的话,结果是 5 2 3 4 1。

## 输入格式

第一行两个正整数 n,m,表示序列长度与操作个数。序列中第 i 项初始为 i。
接下来 m 行,每行两个正整数 l,r,表示翻转的区间。

## 输出格式

输出一行 n个正整数,表示原始序列经过 m 次变换后的结果。

 

题意:维护区间信息,每次会选择一个区间,翻转这个区间,最后输出区间的信息

做法:splay,FHGtreap

 

FHGtreap做法

要翻转l~r区间,就是先打上标记,然后再下传懒标记,首先把整棵树按树的大小分裂成x(1~l - 1), z(l~n), 再将z树分裂成y(l~r), z(r + 1, n),这个时候得到的y树就是整个要操作的区间,最后别忘了把x, y, z树合并返回根节点

#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
#include <unordered_map>
#include <set>
#include <map>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;

#define x first
#define y second

typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef pair<PII, int> PIII;
typedef pair<LL, LL> PLL;
const int N = 100010, M = 50000, mod = 998244353, INF = 0x3f3f3f3f, P = 1e8 - 3;

struct tree{
    int l, r;
    int val, key;
    int lazy, sz;
}tr[N];
int root, cnt;
int l, r;

int newNode(int val)
{
    tr[++ cnt].val = val;
    tr[cnt].key = rand();
    tr[cnt].sz = 1;
    return cnt;
}

void update(int now)
{
    tr[now].sz = tr[tr[now].l].sz + tr[tr[now].r].sz + 1;
}

void pushdown(int now)
{
    swap(tr[now].l, tr[now].r);
    tr[tr[now].l].lazy ^= 1;
    tr[tr[now].r].lazy ^= 1;
    tr[now].lazy = 0;
}

int x, y, z;

//按树的大小分裂
void spilt(int now, int sz, int &x, int &y)
{
    if(!now) x = y = 0;
    else
    {
        if(tr[now].lazy) pushdown(now);//每次可能改变树的结构的操作前都要将懒标记下传
        if(tr[tr[now].l].sz < sz)
        {
            x = now;
            spilt(tr[x].r, sz - tr[tr[now].l].sz - 1, tr[x].r, y);
        }
        else
        {
            y = now;
            spilt(tr[y].l, sz, x, tr[y].l);
        }
        update(now);
    }
}

int merge(int x, int y)
{
    if(!x || !y) return x + y;
    else
    {
        if(tr[x].key > tr[y].key)
        {
            if(tr[x].lazy) pushdown(x);
            tr[x].r = merge(tr[x].r, y);
            update(x);
            return x;
        }
        else
        {
            if(tr[y].lazy) pushdown(y);
            tr[y].l = merge(x, tr[y].l);
            update(y);
            return y;
        }
    }
}

void ldr(int now) //输出中序遍历
{
    if(!now) return;
    if(tr[now].lazy) pushdown(now);//别忘了将剩下的懒标记下传
    ldr(tr[now].l);
    printf("%d ", tr[now].val);
    ldr(tr[now].r);
}

void reverse(int l, int r)
{
    spilt(root, l - 1, x, z);
    spilt(z, r - l + 1, y, z);
    tr[y].lazy ^= 1;
    merge(merge(x, y), z);
}

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    
    for(int i = 1; i <= n; i ++ ) root = merge(root, newNode(i));
    
    while(m -- )
    {
        scanf("%d %d", &l, &r);
        reverse(l, r);
    }
    
    ldr(root);
    
    return 0;
}

 

splay:

把节点l转到根节点,再把r转到l的下面,这个时候节点r的左子树就是要操作的区间的子树,打上懒标记即可

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100010;

struct Node{
    int s[2], p, v;
    int sz, lazy;
    
    void init(int _v, int _p){
        v = _v, p = _p;
        sz = 1;
    }
}tr[N];
int root, idx;
int n, m;

void pushup(int u)
{
    tr[u].sz = tr[tr[u].s[0]].sz + tr[tr[u].s[1]].sz + 1;
}

void pushdown(int u)
{
    if(tr[u].lazy)
    {
        swap(tr[u].s[0], tr[u].s[1]);
        tr[tr[u].s[0]].lazy ^= 1;
        tr[tr[u].s[1]].lazy ^= 1;
        tr[u].lazy = 0;
    }
}

//左右旋
void rotate(int x)
{
    int y = tr[x].p, z = tr[y].p;
    int k = tr[y].s[1] == x;
    tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;
    tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
    tr[x].s[k ^ 1] = y, tr[y].p = x;
    pushup(y), pushup(x);
}

void splay(int x, int k)
{
    while(tr[x].p != k)
    {
        int y = tr[x].p, z = tr[y].p;
        if(z != k)
            if((tr[z].s[1] == y) ^ (tr[y].s[1] == x)) rotate(x);
            else rotate(y);
        rotate(x);
    }
    if(!k) root = x;
}

void insert(int v)
{
    int u = root, p = 0;
    while(u) p = u, u = tr[u].s[v > tr[p].v];
    u = ++ idx;
    if(p) tr[p].s[v > tr[p].v] = u;
    tr[u].init(v, p);
    splay(u, 0);
}

int get_k(int k)
{
    int u = root;
    while(u)
    {
        pushdown(u);
        if(tr[tr[u].s[0]].sz >= k) u = tr[u].s[0];
        else if(tr[tr[u].s[0]].sz + 1 == k) return u;
        else k -= tr[tr[u].s[0]].sz + 1, u = tr[u].s[1];
    }
    return -1;
}

void ldr(int u)
{
    if(!u) return;
    pushdown(u);
    ldr(tr[u].s[0]);
    if(tr[u].v >= 1 && tr[u].v <= n) printf("%d ", tr[u].v);
    ldr(tr[u].s[1]);
}

int main()
{
    cin >> n >> m;
    for(int i = 0; i <= n + 1; i ++ ) insert(i);
    
    while(m -- )
    {
        int l, r;
        cin >> l >> r;
        l = get_k(l), r = get_k(r + 2);
        splay(l, 0), splay(r, l);
        tr[tr[r].s[0]].lazy ^= 1;
    }
    
    ldr(root);
    
    return 0;
}

 

这篇关于文艺平衡树的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!