比STL还STL?——更全面的解析!

如何更快的使用高级数据结构

Part 1 :__gnu_pbds

__gnu_pbds 自带了封装好了的平衡树、字典树、hash等强有力的数据结构,常数还比自己写的小,效率更高

一、平衡树

#define PII pair<int, int>
#define mp_(x, y) make_pair(x, y)
tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> tr;
/// rb_tree_tag 和 splay_tree_tag 选择树的类型(红黑树和伸展树)
null_type//无映射(g++为null_mapped_type)
less<PII>//从小到大排序
tree_order_statistics_node_update//更新方式

tr.insert(mp_(x, y));//插入
tr.erase(mp_(x, y));//删除
tr.order_of_key(PII(x, y));//求排名
tr.find_by_order(x);//找k小值,返回迭代器
tr.join(b);//将b并入tr,前提是两棵树类型一致并且二没有重复元素
tr.split(v, b);//分裂,key小于等于v的元素属于tr,其余属于b
tr.lower_bound(x);//返回第一个大于等于x的元素的迭代器
tr.upper_bound(x);//返回第一个大于x的元素的迭代器
//以上的所有操作的时间复杂度均为O(logn)
//注意,插入的元素会去重,如set

//显然迭代器可以++,--运算

功能不够?自己添加!

我们可以自己定义更新的各种方式
我们需要写一个自己的 node_update(定义 tree 的时候使用自己定义的 update 即可):

template <class Node_CItr, class Node_Itr, class Cmp_Fn, class _Alloc>
struct my_node_update
{
    typedef my_type metadata_type;
    void operator()(Node_Itr it, Node_CItr end_it)
    {
        ...
    }
};

我们先解释一下这个类是如何工作的。节点更新的 tree 都会保存一个 my_type 类型的变量。当我们修改这棵树的时候,会从叶子节点开始修改,并且每次都会调用 operator(),我们来看一下这个函数的两个参数:

Node_Itr it 为调用该函数的元素的迭代器,Node_CItr end_it 可以 const 到叶子节点的迭代器,Node_Itr 有以下的操作:

get_l_child(),返回其左孩子的迭代器,没有则返回 node_end;
get_r_child(),同 get_l_child();
get_metadata(),返回其在树中维护的数据;
**it可以获取it的信息。

为了详细讲解,我们举一个更新子树大小的例子:

template <class Node_CItr, class Node_Itr, class Cmp_FN, class _Alloc>
struct my_node_update
{
    typedef my_type metadata_type;
    // my_type就是指的自己用的类型,例如int,double。。。

    //更新子树大小
    void operator()(Node_Itr it, Node_CItr end_it)
    {
        Node_Itr l = it.get_l_child();
        Node_Itr r = it.get_r_child();
        int left = 0, right = 0;
        if (l != end_it)
        {
            left = l.get_metadata(); //只要有左子节点,就得到左子节点元素大小
        }
        if (r != end_it)
        {
            right = r.get_metadata(); //只要有右子节点,就得到右子节点元素大小
        }
        // const_cast标准准换运算符,用于修改一个const常量
        const_cast<int &>(it.get_metadata()) = left + right + 1;
    }
};

现在我们学会了更新,那么我们该如何自己写操作呢?node_update 所有 public 方法都会在树中公开。如果我们在 node_update 中将它们声明为 virtual ,则可以访问基类中的所有 virtual。所以,我们在类里添加以下内容:

virtual Node_CItr node_begin() const=0;
virtual Node_CItr node_end() const=0;

这样我们就能直接访问树了,还有,node_begin指向树根,node_end指向最后一个叶子节点的后一个地址,下面这个就是查排名的操作:

int myrank(int x)
{
    int ans = 0;
    Node_CItr it = node_begin();
    while (it != node_end())
    {
        Node_CItr l = it.get_l_child();
        Node_CItr r = it.get_r_child();
        if (Cmp_Fn()(x, **it))
        {
            it = l;
        }
        else
        {
            ans++;
            if (l != node_end())
            {
                ans += l.get_metadata();
            }
            it = r;
        }
    }
    return ans;
}
template <class Node_CItr, class Node_Itr, class Cmp_Fn, class _Alloc>
struct my_node_update
{
    virtual Node_CItr node_begin() const = 0;
    virtual Node_CItr node_end() const = 0;
    typedef int metadata_type;
    //更新节点权值
    inline void operator()(Node_Itr it, Node_CItr end_it)
    {
        Node_Itr l = it.get_l_child(), r = it.get_r_child();
        int left = 0, right = 0;
        if (l != end_it)
        {
            left = l.get_metadata();
        }
        if (r != end_it)
        {
            right = r.get_metadata();
        }
        const_cast<metadata_type &>(it.get_metadata()) = left + right + (*it)->second;
    }
    //平衡树上二分
    inline int prefix_sum(int x)
    {
        int ans = 0;
        Node_CItr it = node_begin();
        while (it != node_end())
        {
            Node_CItr l = it.get_l_child(), r = it.get_r_child();
            if (Cmp_Fn()(x, (*it)->first))
            {
                it = l;
            }
            else
            {
                ans += (*it)->second;
                if (l != node_end())
                {
                    ans += l.get_metadata();
                }
                it = r;
            }
        }
        return ans;
    }
    inline int interval_sum(int l, int r)
    {
        return prefix_sum(r) - prefix_sum(l - 1);
    }
} 
int main()
{
    tree<int, int, std ::less<int>, rb_tree_tag, my_node_update> T;
    T[2] = 100;
    T[3] = 1000;
    T[4] = 10000;
    printf("%d\n", T.interval_sum(3, 4));
    printf("%d\n", T.prefix_sum(3));
}

例题:CF459D Pashmak and Parmida’s problem

标答应该是先离散化,然后求前后缀和预处理,之后用树状数组求一下逆序对。

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace std;
using namespace __gnu_pbds;

tree<pair<int, int>, null_type, less<pair<int, int>>, rb_tree_tag, my_node_update> me;

template <class Node_CItr, class Node_Itr, class Cmp_Fn, class _Alloc>
struct my_node_update
{
    typedef int metadata_type;
    int order_of_key(pair<int, int> x)
    {
        int ans = 0;
        Node_CItr it = node_begin();
        while (it != node_end())
        {
            Node_CItr l = it.get_l_child();
            Node_CItr r = it.get_r_child();
            if (Cmp_Fn()(x, **it))
            {
                it = l;
            }
            else
            {
                ans++;
                if (l != node_end())
                {
                    ans += l.get_metadata();
                }
                it = r;
            }
        }
        return ans;
    }
    void operator()(Node_Itr it, Node_CItr end_it)
    {
        Node_Itr l = it.get_l_child();
        Node_Itr r = it.get_r_child();
        int left = 0, right = 0;
        if (l != end_it)
        {
            left = l.get_metadata();
        }
        if (r != end_it)
        {
            right = r.get_metadata();
        }
        const_cast<int &>(it.get_metadata()) = left + right + 1;
    }
    virtual Node_CItr node_begin() const = 0;
    virtual Node_CItr node_end() const = 0;
};

int main()
{
    map<int, int> cnt[2];
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    vector<int> pre(n), suf(n);
    for (int i = 0; i < n; i++)
    {
        pre[i] = cnt[0][a[i]]++;
        suf[n - i - 1] = cnt[1][a[n - i - 1]]++;
    }
    long long ans = 0;
    for (int i = 1; i < n; i++)
    {
        me.insert({pre[i - 1], i - 1});
        ans += i - me.order_of_key({suf[i], i});
    }
    cout << ans << endl;
    return 0;
}

二、字典树

trie 即为字典树,我们先看如何定义一个 trie 与它的操作:

typedef trie<string,null_type,trie_string_access_traits<>,pat_trie_tag,trie_prefix_search_node_update> tr;
//第一个参数必须为字符串类型,tag也有别的tag,但pat最快,与tree相同,node_update支持自定义
tr.insert(s); //插入s 
tr.erase(s); //删除s 
tr.join(b); //将b并入tr 
pair//pair的使用如下:
pair<tr::iterator,tr::iterator> range=base.prefix_range(x);
for(tr::iterator it=range.first;it!=range.second;it++)
    cout<<*it<<' '<<endl;
//pair中第一个是起始迭代器,第二个是终止迭代器,遍历过去就可以找到所有字符串了。 

三、哈希

hash_table 的用法与 map 类似,它是这么定义的:

cc_hash_table<int,bool> h;
gp_hash_table<int,bool> h;

其中 cc 开头为拉链法,gp 开头为探测法,个人实测探测法稍微快一些。

操作和 map 差不多,支持 [ ]find

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
gp_hash_table<string, int> h;
void judge(string s)
{
    if (h.find(s) != h.end())
        cout << "orz %%%";
    else
        cout << "tan90";
    cout << endl;
}
int main()
{
    h["Ican'tAKIOI"] = 1;
    h.insert(make_pair("UAKIOI", 1));
    string str;
    while (cin >> str)
        judge(str);
    return 0;
}

注意:hash_table 的总时间复杂度仅为 \(O ( n )\)

四、堆

priority_queue 为优先队列,用堆实现,priority_queue 的定义与操作 :

priority_queue<int,greater<int>,TAG> Q;//小根堆,大根堆写less<int>
/*其中的TAG为类型,有以下几种:
pairing_heap_tag
thin_heap_tag
binomial_heap_tag
rc_binomial_heap_tag 
binary_heap_tag
其中pairing_help_tag最快*/
Q.push(x);
Q.pop();
Q.top();
Q.join(b);
Q.empty();
Q.size(); 
Q.modify(it,6);
Q.erase(it);
//以上操作我都不讲了,pbds里的优先队列还可以用迭代器遍历

Part 2 :__gnu_cxx

0.前言

这玩意儿核心就是 rope 功能

可以当作可持久化平衡树使用,内部构造是一个块状链表

1.声明

1)头文件

#include<ext/rope>

2)调用命名空间

using namespace __gnu_cxx;

3)声明使用

rope<int>Irp;

rope<long long>Lrp;

crope crp;//相当于定义成rope<char>,即定义为string类型

rope<char>rop;

rope<double>Drp;

//rope<PII>Prp;不合法

2. 支持操作

push_back(x);	  在末尾添加x

insert(pos,x);	  在pos插入x

erase(pos,x);	  从pos开始删除x个

replace(pos,x);	  从pos开始换成x

substr(pos,x);	  提取pos开始x个
 
at(x)/[x];	      访问第x个元素

test.clear();     清空元素

3. char 类型的 rope

insert(int pos, string &s, int n);
将字符串 s 的前 n 位插入 rope 的下标 pos

append(string &s,int pos,int n);
把字符串 s 中从下标 pos 开始的 n 个字符连接到 rope 的结尾,如没有参数 n 则把字符串 s 中下标 pos 后的所有字符连接到 rope 的结尾,如没有参数 pos 则把整个字符串 s 连接到 rope 的结尾

substr(int pos, int len);
提取 rope 的从下标 pos 开始的 len 个字符

at(int x);
访问 rope 的下标为 x 的元素

erase(int pos, int num);
rope 的下标 pos 开始删除 num 个字符

copy(int pos, int len, string &s);
rope 的下标 pos 开始的 len 个字符用字符串 s 代替,如果 pos 后的位数不够就补足

replace(int pos, string &x);
rope 的下标 pos开始替换成字符串xx的长度为从pos开始替换的位数,如果pos` 后的位数不够就补足

int n, m;
char a[N];
rope<char> r;
int main()
{
    for (int i = 0; i <= 10; ++i)
        a[i] += i + '0';
    r.insert(0, a + 2, 8);
    for (int i = 0; i <= 10; ++i)
        cout << r.at(i) << " ";
    puts("");
    for (int i = 0; i <= 10; ++i)
        cout << r[i] << " ";
    puts("");
    return 0;
}
2 3 4 5 6 7 8 9
2 3 4 5 6 7 8 9

4.int 类型的 rope

insert(int pos, int *s, int n);
int 数组(以下的 s 都是 int 数组)s 的前 n 位插入 rope 的下标 pos 处,如没有参数 n 则将数组 s 的所有位都插入 rope 的下标 pos

append(int *s,int pos,int n);
把数组 s 中从下标 pos 开始的 n 个数连接到 rope 的结尾,如没有参数 n 则把数组 s 中下标 pos 后的所有数连接到 rope 的结尾,如没有参数 pos 则把整个数组 s 连接到 rope 的结尾

substr(int pos, int len);
提取 rope 的从下标 pos 开始的 len 个数

at(int x);
访问 rope 的下标为 x 的元素

erase(int pos, int num);
rope 的下标 pos 开始删除 num 个数

copy(int pos, int len, int *s);
rope 的下标 pos 开始的 len 个数用数组 s 代替,如果 pos 后的位数不够就补足

replace(int pos, int *x);
rope 的下标 pos 开始替换成数组 xx 的长度为从 pos 开始替换的位数,如果 pos 后的位数不够就补足

rope<int> rp;
int main()
{
    rp.append(3);
    rp.append(1);
    rp.append(2);
    rp.append(1);
    rp = rp.substr(1, 3); //从1开始截3个数字,注意rope是从0开始的,所有的容器都是从0开始的
    for (int i = 0; i < rp.size(); ++i)
        cout << rp[i] << " ";
    puts("");
    return 0;
}
1 2 1

5. 具体的细节

时间复杂度: \(O(n\sqrt{n})\) 因为是用块状链表实现的)

空间复杂度: \(O(\sqrt{n})\)

6.可持久化

定义方法

rope<char> *now[p];

如何申请更新一个新的可持久化版本:

now[0]=new rope<char>();

如何继承版本

now[cnt]=new rope<char>(*now[cnt-1]);

如何回到查询过去的版本

ans=now[cnt]->at(num);

Part 3 :实战代码样例

例1 :P3369 【模板】普通平衡树

注:此题两个代码不吸氧都可以随便跑

1. rb_tree_tag

因为 tree 里不能有相同的数,但是实际会插入相同的数,所以把这些数左移 $20 $位在加上一个常数操作,这样就保证了在不影响相对大小关系的情况下,消除了相同的数,因为是 long long ,是 \(2^64 - 1\),而数据是 \(1e7\)。也就是小于 \(2^{24}\),我们左移 \(20\) 位,最多是 \(2^{44}<2^{64} - 1\) ,我们左移 \(20\) 位加一个不大且都不相同的常数,这样使得整个数据都不相等,而且我们右移 \(20\) 位输出答案的时候,加的那个不相同的常数会被右移挤掉,所以原来的数是多少,左移 \(20\) 位加上一个常数(小于 \(2^{20}\) 再右移,这个数是不会变的,还成功完成了去重!!!。

#include <bits/stdc++.h>
#include <bits/extc++.h>

typedef long long ll;

const int N = 2e5 + 7, M = 1e5 + 7, INF = 0x3f3f3f3f;

using namespace std;
using namespace __gnu_pbds;

tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> tr;
int n, m;
ll k, ans;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        int op;
        cin >> op >> k;

        if (op == 1)
        {
            tr.insert((k << 20) + i);
        }

        if (op == 2)
        {
            tr.erase(tr.lower_bound(k << 20));
        }

        if (op == 3)
        {
            printf("%d\n", tr.order_of_key(k << 20) + 1);
        }

        if (op == 4)
        {
            ans = *tr.find_by_order(k - 1);
            printf("%lld\n", ans >> 20);
        }

        if (op == 5)
        {
            ans = *--tr.lower_bound(k << 20);
            printf("%lld\n", ans >> 20);
        }

        if (op == 6)
        {
            ans = *tr.upper_bound((k << 20) + n);
            printf("%lld\n", ans >> 20);
        }
    }
    return 0;
}

2. splay_tree_tag

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>

using namespace std;
using namespace __gnu_pbds;

#define Node pair<int, int>

map<int, int> s;
tree<Node, null_type, less<Node>, splay_tree_tag, tree_order_statistics_node_update> T;

int n, op, x;

int main()
{
    scanf("%d", &n);
    for (register int i = 1; i <= n; i++)
        switch (scanf("%d%d", &op, &x), op)
        {
        case 1:
            T.insert(Node(x, s[x]++));
            break;
        case 2:
            T.erase(Node(x, --s[x]));
            break;
        case 3:
            printf("%d\n", (int)T.order_of_key(Node(x, 0)) + 1);
            break;
        case 4:
            printf("%d\n", T.find_by_order(x - 1)->first);
            break;
        case 5:
            printf("%d\n", T.find_by_order(T.order_of_key(Node(x, 0)) - 1)->first);
            break;
        case 6:
            printf("%d\n", T.find_by_order(T.order_of_key(Node(x, s[x] - 1)) + (T.find(Node(x, 0)) == T.end() ? 0 : 1))->first);
            break;
        default:
            break;
        }
    return 0;
}

例2 :P3919 【模板】可持久化线段树

此代码不吸氧有 80pts

#include <bits/stdc++.h>
#include <bits/extc++.h>

const int N = 1e6 + 1, M = 1e5 + 7, INF = 0x3f3f3f3f;

using namespace std;
using namespace __gnu_cxx;

int n, m;
int a[N];
rope<int> *S[N];

int main()
{
    cin >> n >> m;
    S[0] = new rope<int>();
    S[0]->append(0);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    S[0]->insert(1, a, n);

    for (int i = 1; i <= m; i++)
    {
        int v, k, a, b;
        cin >> v >> k;
        S[i] = new rope<int>(*S[v]);
        if (k == 1)
        {
            cin >> a >> b;
            S[i]->replace(a, b);
        }
        else
        {
            cin >> a;
            printf("%d\n", S[i]->at(a));
        }
    }
    return 0;
}

例3 : P3835 【模板】可持久化平衡树

无O2 ,可得 92pts

#include <bits/stdc++.h>
#include <ext/rope>

#define rint register int
#define endl '\n'

#define lb lower_bound
#define ub upper_bound

using namespace std;
using namespace __gnu_cxx;

const int N = 5e5 + 5;
const int inf = 0x3f3f3f3f;

rope<int> *a[N];
int n;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n;
    a[0] = new rope<int>();

    for (rint i = 1; i <= n; i++)
    {
        int v, op, x;
        cin >> v >> op >> x;

        a[i] = new rope<int>(*a[v]);

        if (op == 1)
        {
            a[i]->insert(lb(a[i]->begin(), a[i]->end(), x) - a[i]->begin(), x);
        }
        if (op == 2)
        {
            auto it = lb(a[i]->begin(), a[i]->end(), x);
            if (it != a[i]->end() && *it == x)
            {
                a[i]->erase(it - a[i]->begin(), 1);
            }
        }
        if (op == 3)
        {
            cout << (int)(lb(a[i]->begin(), a[i]->end(), x) - a[i]->begin()) + 1 << endl;
        }
        if (op == 4)
        {
            cout << *(a[i]->begin() + x - 1) << endl;
        }
        if (op == 5)
        {
            auto it = lb(a[i]->begin(), a[i]->end(), x);
            if (it == a[i]->begin() - 1)
            {
                cout << -inf << endl;
            }
            else
            {
                it--;
                cout << *it << endl;
            }
        }
        if (op == 6)
        {
            auto it = ub(a[i]->begin(), a[i]->end(), x);
            if (it == a[i]->end())
            {
                cout << inf << endl;
            }
            cout << *it << endl;
        }
    }

    return 0;
}

例4 : P3402 可持久化并查集

吸氧可 AC

#include <bits/stdc++.h>
#include <ext/rope>

//#define int long long
#define endl '\n'
#define rint register unsigned

using namespace __gnu_cxx;
using namespace std;

#define IN stdin->_IO_read_ptr < stdin->_IO_read_end ? *stdin->_IO_read_ptr++ : __uflow(stdin)
#define OUT(_ch) stdout->_IO_write_ptr < stdout->_IO_write_end ? *stdout->_IO_write_ptr++ = _ch : __overflow(stdout, _ch)

const int N = 2e5 + 5;

unsigned f[N];
unsigned r[N];

void read(unsigned &x)
{
    x = 0;
    char ch = IN;
    while (ch < 47) ch = IN;
    while (ch > 47) x = (x << 1) + (x << 3) + (ch & 15), ch = IN;
}

unsigned find(rope<unsigned> &fa, unsigned &i)
{
    unsigned f = fa[i];
    return f == i ? i : find(fa, f);
}

void merge(rope<unsigned> &fa, unsigned &a, unsigned &b)
{
    a = find(fa, a);
    b = find(fa, b);
	
    if (a == b)
    {
	return ;
    }
    if (r[a] > r[b])
    {
        fa.replace(b, a);		
    }
    else
    {
        if (r[a] == r[b])
        {
            r[b]++;			
	}
    fa.replace(a, b);
    }
}

signed main()
{
    unsigned n, m, op, a, b;
    read(n);
    read(m);
    rope<unsigned> fa[m + 1];
    
    for (rint i = 1; i <= n; i++)
    {
	f[i] = i;
    }    
    
	f[0] = 1;
    fa[0] = rope<unsigned>(f);
    
    for (rint i = 1; i <= m; i++)
    {
        read(op);
        
        switch (op)
        {
        case 1:
            fa[i] = fa[i - 1];
            read(a);read(b);
            merge(fa[i], a, b);
            break;
        case 2:
            read(a);
            fa[i] = fa[a];
            break;
        default:
            fa[i] = fa[i - 1];
            read(a);read(b);
            OUT(find(fa[i], a) == find(fa[i], b) ? '1' : '0');
            puts("");
        }
    }
    
    return 0;
}

例5 : P3380 【模板】二逼平衡树(树套树)

吸氧可 AC

#include <bits/extc++.h>
#include <bits/stdc++.h>

#define endl '\n'

using namespace std;
using namespace __gnu_pbds;

const int N = 5e4 + 5;
const int inf = 2147483647;
int n, m, a[N];

tree<pair<int, int>, null_type, less<pair<int, int>>, rb_tree_tag, tree_order_statistics_node_update> t[N << 2];
int p[N];

void build(int u, int l, int r)
{
    for (int j = l; j <= r; j++)
    {
        t[u].insert(make_pair(a[j], j));
    }
    if (l == r)
    {
        p[l] = u;
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
}

void change(int x, int v)
{
    for (int i = p[x]; i; i >>= 1)
    {
        t[i].erase(t[i].find(make_pair(a[x], x)));
        t[i].insert(make_pair(v, x));
    }
    a[x] = v;
}

int get_rank(int u, int l, int r, int x, int y, int z)
{
    if (x <= l && y >= r)
    {
        return t[u].order_of_key(make_pair(z, 0));
    }
    int ans = 0;
    int mid = (l + r) >> 1;
    if (x <= mid)
    {
        ans = get_rank(u << 1, l, mid, x, y, z);
    }
    if (y > mid)
    {
        ans += get_rank(u << 1 | 1, mid + 1, r, x, y, z);
    }
    return ans;
}

int get_prev(int u, int l, int r, int x, int y, int z)
{
    if (x <= l && y >= r)
    {
        int v = t[u].order_of_key(make_pair(z, 0));
        if (!v)
        {
            return -inf;
        }
        return t[u].find_by_order(v - 1)->first;
    }

    int mid = (l + r) >> 1;

    if (y <= mid)
    {
        return get_prev(u << 1, l, mid, x, y, z);
    }
    if (x > mid)
    {
        return get_prev(u << 1 | 1, mid + 1, r, x, y, z);
    }

    return max(get_prev(u << 1, l, mid, x, y, z), get_prev(u << 1 | 1, mid + 1, r, x, y, z));
}

int get_next(int u, int l, int r, int x, int y, int z)
{
    if (x <= l && y >= r)
    {
        int v = t[u].order_of_key(make_pair(z, inf));
        if (v == r - l + 1)
        {
            return inf;
        }
        return t[u].find_by_order(v)->first;
    }

    int mid = (l + r) >> 1;
    if (y <= mid)
    {
        return get_next(u << 1, l, mid, x, y, z);
    }
    if (x > mid)
    {
        return get_next(u << 1 | 1, mid + 1, r, x, y, z);
    }
    return min(get_next(u << 1, l, mid, x, y, z), get_next(u << 1 | 1, mid + 1, r, x, y, z));
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m;

    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    build(1, 1, n);

    while (m--)
    {
        int op;
        cin >> op;

        if (op == 3)
        {
            int x, v;
            cin >> x >> v;
            change(x, v);
            continue;
        }

        int l, r, k;
        cin >> l >> r >> k;

        if (op == 1)
        {
            cout << get_rank(1, 1, n, l, r, k) + 1;
        }
        if (op == 2)
        {
            int L = 0, R = 1e8;
            int ans = 0;
            while (L <= R)
            {
                int mid = (L + R) >> 1;
                if (get_rank(1, 1, n, l, r, mid) < k)
                {
                    ans = mid;
                    L = mid + 1;
                }
                else
                {
                    R = mid - 1;
                }
            }
            cout << ans;
        }
        if (op == 4)
        {
            cout << get_prev(1, 1, n, l, r, k);
        }
        if (op == 5)
        {
            cout << get_next(1, 1, n, l, r, k);
        }
        cout << endl;
    }
    return 0;
}

例6 :P4779 【模板】单源最短路径

跑的比普通堆优化快得多

#include <bits/stdc++.h>
#include <bits/extc++.h>

#define rint register int
#define endl '\n'

using namespace std;

const int N = 1e5 + 5;
const int M = 4e5 + 5;
const int inf = 0x3f3f3f3f;

int n, m, s;
int h[N], idx;
int e[M], w[M], ne[M];
int dist[N];

void add(int a, int b, int c)
{
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

__gnu_pbds::priority_queue<pair<int, int>, greater<pair<int, int> > > q;
__gnu_pbds::priority_queue<pair<int, int>, greater<pair<int, int> > >::point_iterator it[N];

void dijkstra()
{
    q.clear();
    it[s] = q.push(make_pair(0, s));
    dist[s] = 0;
    
    for (rint i = 2; i <= n; i++)
    {
        dist[i] = inf;
	it[i] = q.push(make_pair(inf, i));		
    }

    while (!q.empty())
    {
        int x = q.top().second;
        q.pop();
        for (rint i = h[x]; i; i = ne[i])
        {
            int y = e[i];
            int z = w[i];
	    if (dist[y] > dist[x] + z)
            {
                dist[y] = dist[x] + z;
                q.modify(it[y], make_pair(dist[x] + z, y));
            }			
	}
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m >> s;

    for (rint i = 1; i <= m; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }

    dijkstra();

    for (rint i = 1; i <= n; i++)
    {
        cout << dist[i] << " ";
    }

    return 0;
}

例7 : P1379 八数码难题

不开O2稳过

#include <bits/stdc++.h>
#include <bits/extc++.h>

#define ll long long

using namespace std;
using namespace __gnu_pbds;

ll dx[4]{0, 0, 1, -1};
ll dy[4]{1, -1, 0, 0};
ll s, e;
gp_hash_table<ll, ll> ans;
gp_hash_table<int, int> d;
queue<ll> q;
int c[3][3];

int bfs(int n)
{
    q.push(n);
    q.push(e);
    ans[n] = 0;
    ans[e] = 1;
    d[n] = 2;
    d[e] = 1;
    while (!q.empty())
    {
        ll now = q.front();
        q.pop();
        int x = 0, y = 0;
        ll u = now;
        for (int i = 2; i >= 0; i--)
        {
            for (int j = 2; j >= 0; j--)
            {
                c[i][j] = u % 10;
                u /= 10;
                if (!c[i][j])
                {
                    x = i;
                    y = j;
                }
            }
        }
        for (int i = 0; i < 4; i++)
        {
            ll xx = x + dx[i], yy = y + dy[i], nxt = 0;
            if (xx < 0 || xx > 2 || yy < 0 || yy > 2)
                continue;
            swap(c[xx][yy], c[x][y]);
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    nxt = nxt * 10 + c[i][j];
                }
            }

            if (d[nxt] + d[now] == 3)
            {
                return ans[nxt] + ans[now];
            }
            if (d[nxt] == d[now])
            {
                swap(c[xx][yy], c[x][y]);
                continue;
            }
            swap(c[xx][yy], c[x][y]);
            ans[nxt] = ans[now] + 1;
            d[nxt] = d[now];
            q.push(nxt);
        }
    }
}

int main()
{
    cin >> s;
    e = 123804765;
    if (s == e)
    {
        cout << 0 << endl;
    }
    else
    {
        cout << bfs(s) << endl;
    }
    return 0;
}

例8 :P3224 [HNOI2012]永无乡

不开O2稳过

#include <bits/stdc++.h>
#include <bits/extc++.h>

using namespace std;
using namespace __gnu_pbds;

inline int read()
{
    int r = 0;
    char c = getchar();
    while (c < '0' || c > '9')
        c = getchar();
    while (c >= '0' && c <= '9')
    {
        r = r * 10 + c - '0';
        c = getchar();
    }
    return r;
}

typedef tree<int, int, less<int>, rb_tree_tag, tree_order_statistics_node_update> Tr;
const int M = 1e5 + 5;
Tr T[M];
Tr::point_iterator it;
int fa[M];

int find(int a)
{
    if (fa[a] == a)
        return a;
    return fa[a] = find(fa[a]);
}

void merge(int a, int b)
{
    a = find(a);
    b = find(b);
    if (a == b)
        return;
    if (T[a].size() > T[b].size())
        swap(a, b);
    for (it = T[a].begin(); it != T[a].end(); it++)
        T[b].insert(*it);
    fa[a] = b;
}

int main()
{
    int n, m, a, b;
    char c;
    n = read();
    m = read();
    for (int i = 1; i <= n; i++)
        T[i].insert(make_pair(read(), i)), fa[i] = i;
    while (m--)
        merge(read(), read());
    m = read();
    while (m--)
    {
        scanf(" %c", &c);
        a = read();
        b = read();
        if (c == 'B')
            merge(a, b);
        else
        {
            a = find(a);
            if (b > T[a].size())
                puts("-1");
            else
                printf("%d\n", T[a].find_by_order(b - 1)->second);
        }
    }
    return 0;
}

例9 : UVA11525 Permutation

#include <bits/stdc++.h>
#include <bits/extc++.h>

using namespace std;
using namespace __gnu_pbds;

tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> tr;
int T;

int main()
{
    cin >> T;
    while (T--)
    {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
            tr.insert(i);
        for (int i = 1, x; i <= n; i++)
        {
            cin >> x;
            auto it = tr.find_by_order(x);
            cout << *it << "\n "[i < n];
            tr.erase(it);
        }
    }
    return 0;
}

例10 :P2286 [HNOI2004]宠物收养场

不开O2稳过

#include <bits/stdc++.h>
#include <bits/extc++.h>

using namespace std;
using namespace __gnu_pbds;

tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> T[2];
typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update>::iterator ITER;
int n, op, a, ans;

bool empty(const int &x) { 
    return T[x].lower_bound(-2147483647) == T[x].end() ? true : false; 
}

int Abs(const int &x) { 
    return x < 0 ? (-x) : x; 
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d", &op, &a);
        if (empty(op ^ 1))
            T[op].insert(a);
        else
        {
            ITER it = T[op ^ 1].lower_bound(a);
            if ((*it) == a)
                T[op ^ 1].erase(it);
            else
            {
                ITER it2 = T[op ^ 1].upper_bound(a);
                if (it == T[op ^ 1].begin())
                {
                    ans = (ans + Abs(a - (*it2))) % 1000000;
                    T[op ^ 1].erase(it2);
                    continue;
                }
                it--;
                if (it2 == T[op ^ 1].end())
                {
                    ans = (ans + Abs(a - (*it))) % 1000000;
                    T[op ^ 1].erase(it);
                    continue;
                }
                if (Abs(a - (*it2)) < Abs(a - (*it)))
                {
                    ans = (ans + Abs(a - (*it2))) % 1000000;
                    T[op ^ 1].erase(it2);
                    continue;
                }
                else
                {
                    ans = (ans + Abs(a - (*it))) % 1000000;
                    T[op ^ 1].erase(it);
                }
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

例11 :P2596 [ZJOI2006]书架

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace std;
using namespace __gnu_pbds;

tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> T;
typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update>::iterator ITER;
int Pos[100001], Val[500001], a, x, n, m;
char op[7];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &Val[i + 100000]);
        Pos[Val[i + 100000]] = i + 100000;
        T.insert(i + 100000);
    }
    for (int i = 1; i <= m; i++)
    {
        scanf("%s", op);
        if (op[0] == 'T')
        {
            scanf("%d", &a);
            int tmp = *T.find_by_order(0);
            T.erase(T.lower_bound(Pos[a]));
            Pos[a] = tmp - 1;
            Val[tmp - 1] = a;
            T.insert(tmp - 1);
        }
        else if (op[0] == 'B')
        {
            scanf("%d", &a);
            int tmp = *T.find_by_order(n - 1);
            T.erase(T.lower_bound(Pos[a]));
            Pos[a] = tmp + 1;
            Val[tmp + 1] = a;
            T.insert(tmp + 1);
        }
        else if (op[0] == 'I')
        {
            scanf("%d%d", &a, &x);
            if (x == 0)
                continue;
            ITER it = T.lower_bound(Pos[a]);
            ITER it_Beside = it;
            if (x == 1)
                it_Beside++;
            else
                it_Beside--;
            swap(Val[*it], Val[*it_Beside]);
            swap(Pos[Val[*it]], Pos[Val[*it_Beside]]);
        }
        else if (op[0] == 'A')
        {
            scanf("%d", &a);
            printf("%d\n", T.order_of_key(Pos[a]));
        }
        else
        {
            scanf("%d", &a);
            printf("%d\n", Val[*T.find_by_order(a - 1)]);
        }
    }
    return 0;
}
posted @ 2022-08-21 23:25  PassName  阅读(55)  评论(0编辑  收藏  举报