比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开始替换成字符串
x,
x的长度为从
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
开始替换成数组 x
,x
的长度为从 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;
}
比STL还STL?——更全面的解析!的更多相关文章
- [洛谷日报第39期]比STL还STL?——pbds
[洛谷日报第39期]比STL还STL?——pbds 洛谷科技 发布时间:18-08-3116:37 __gnu_pbds食用教程 引入 某P党:“你们C++的STL库真强(e)大(xin),好多数 ...
- STL库list::sort()实现深度解析
原创,转载请注明出处:STL库list::sort()实现深度解析 list模板的定义以及一些基本成员函数的实现这里我就不赘述了,还不清楚的同学可以到网上查找相关资料或者直接查看侯捷翻译的<ST ...
- C++11 STL Regex正则表达式与字符串字段解析
简单的日期正则表达式 一个简单的日期解析程序,从yyyy-mm-dd格式的日期字符串中,分别获取年月日. 先设置一个简单的正则表达式,4位数字的"年",1-2位数字的"月 ...
- 组合数学 + STL --- 利用STL生成全排列
Ignatius and the Princess II Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K ( ...
- 比JSONKit还要快的第三方JSON解析器NextiveJson
这款比JSONKit还好用,效率跟iOS5原生的差不多,不过解析后对内存的释放比原生的要多.所以推荐 https://github.com/nextive/NextiveJson 顺便提一下解析XML ...
- Python 3.12 目标:还可以更快!
按照发布计划,Python 3.11.0 将于 2022 年 10 月 24 日发布. 据测试,3.11 相比于 3.10,将会有 10-60% 的性能提升,这个成果主要归功于"Faster ...
- STL:STL各种容器的使用时机详解
C++标准程序库提供了各具特长的不同容器.现在的问题是:该如何选择最佳的容器类别?下表给出了概述. 但是其中有些描述可能不一定实际.例如:如果你需呀处理的元素数量很少,可以虎落复杂度,因为线性算法通常 ...
- XML解析——SAX解析以及更方便的解析工具(JDOM、DOM4J)
XML主要用于数据交换,HTML则用于显示. 相对于DOM的树形解析,SAX采用的是顺序解析,这种解析方法可以快速地读取XML数据的方式. SAX主要事件: No. 方法 类型 描述 1 public ...
- [stl] SGI STL的空间配置器
第一级空间配置器 第一级配置以malloc(), free(), realloc()等c函数执行实际的内存配置,释放.重配置操作,并实现出类似c++ new handler的机制.它不能直接使用c++ ...
- DL,DT,DD,比传统table更语义,解析更快的table列表方式
使用dl,dt,dd替代传统的table布局 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" & ...
随机推荐
- .Net Core控制台程序读取Json配置文件
1 添加依赖(可以直接通过nuget包安装 Microsoft.Extensions.Configuration.Json 这个会依赖其他两个会自动安装依赖的) Microsoft.Extension ...
- 这样拆分和压缩css代码
在[拆分]和[压缩]css代码之前,首先要配置 loader 处理不同的 css 资源,因为 webpack 没有默认可处理 css 资源的规则,具体可参考这一篇 webpack处理css/less资 ...
- vite 找不到依赖模块:[plugin:vite:dep-pre-bundle]
问题描述: 运行项目时,出现[plugin:vite:dep-pre-bundle] 错误.这种问题一般为依赖的包未正常配置相关字段,导致vite无法找到包的入口. 遇到这种模块内.找不到引用模块的, ...
- 为什么 API 治理需要内部倡导
API 治理旨在帮助人们通过 API 实现最大价值.但是,只有了解 API 是什么以及 API 的重要性,并且认识到 API 治理是在帮助他们而不是监管他们,才能实现这一目标.这就是为什么在任何 AP ...
- Java stream 流
Java stream 流 中间操作 1.filter 作用:将流中的元素,基于自定义的比较器进行去重 方法定义 Stream<T> filter(Predicate<? super ...
- Linq关联两个DataTable合并为一个DataTable
DataSet ds ; DataTable dt1= ds.Tables[0]; DataTable dt2= ds.Tables[1]; //关联 var res = from m in dt1. ...
- HTML一键打包EXE工具1.9.9发布 (包含最新版下载地址)
HTML一键打包EXE工具(HTML封装EXE,桌件)是一款能将任意HTML项目(网址)打包为单个EXE文件的工具,无需依赖浏览器和服务器,直接双击即可运行.该工具支持多种HTML项目类型,包括KRP ...
- numpy 索引,切片 ,转置,变值,多个数组的拼接
- SVN: Could not resolve hostname 解决方法_
svn 报错如下 org.tigris.subversion.javahl.ClientException: RA layer request failed svn: OPTIONS of '[你的s ...
- [SUCTF 2019]CheckIn 1
看到字符upload 就想到了文件上传漏洞 就先上传一个一句话木马试试 似乎直接上传不成功,可能是有什么过滤 再上传一个包含一句话木马的图片试试 发现提示不能是图片,这时候就不会了,在网上找了一下wp ...