c++的一些科技
pd_ds
需要
#include<bits/extc++.h>
using namespace __gnu_pbds;
using namespace __gnu_cxx;
__gnu_pbds::priority_queue
注意可能会与std::priority_queue
冲突。
定义方法:__gnu_pbds ::priority_queue<T, Compare, Tag, Allocator>
-
T:类型名
-
Compare:严格弱化的比较类型
-
Tag:
__gnu_pbds
提供的五种堆,默认为pairing_heap_tag
-
pairing_heap_tag
:配对堆(配对堆是一个支持插入,查询/删除最小值,合并,修改元素等操作的数据结构,是一种可并堆。有速度快和结构简单的优势,但由于其为基于势能分析的均摊复杂度,无法可持久化)。在实际测试中表现最好。 -
binary_heap_tag
:二叉堆,表现并不是如官方文档说的那么好,在原生元素中表现可能要比配对堆好(用模板题排序测得,在luogu(c++14(GCC9))要比配对堆快几十毫秒)。 -
binomial_heap_tag
:二项堆(二项堆(binomial heap)是一种类似于二叉堆的堆结构。与二叉堆相比,其优势是可以快速合并两个堆,因此它属于可合并堆(mergeable heap)抽象数据类型的一种。),合并操作时表现优于二叉堆,取堆顶元素的复杂度比二叉堆高。 -
rc_binomial_heap_tag
:冗余计数二项堆,表现貌似比二项堆好点。 -
thin_heap_tag
:除了合并是\(O(n)\)的,其它和斐波那契堆一样。综合来看,Tag中默认的
pairing_heap_tag
综合表现最好,竞赛中常用的也是这个。
-
-
Allocator:空间配置器,不造是啥,反正在竞赛中基本不会设计,想了解的大佬建议百度
成员函数:
- push():向堆中压入一个元素并返回改元素位置的迭代器
- pop():弹出堆顶
- top():返回堆顶元素
- size():返回元素个数
- empty():返回是否为空
- modify(iterator ,key)将迭代器位置的
key
变为传入的key
,并排序 - erase(iterator):删除迭代器处的键值
- join(__gnu_pbds::priority_queue &other):将
other
合并到*this
中并将other
清空
\(\begin{array}{c|ccccc} {} & \text{push} & \text{pop} &\text{modify} &\text{erase} &\text{join}\\ \hline pairing\_heap\_tag& O(1) &均摊 \Theta(\log n)& 均摊\Theta(\log n)&均摊\Theta(\log n)&O(1) \\ binary\_heap\_tag&\Theta(\log n)&均摊\Theta(\log n)& \Theta(n)&\Theta(n)&\Theta(n)\\ binomial\_heap\_tag&均摊O(1)&\Theta(\log n)&\Theta(\log n)&\Theta(\log n)&\Theta(\log n)\\ rc\_binomial\_heap\_tag&O(1)&\Theta(\log n)&\Theta(\log n)&\Theta(\log n)&\Theta(\log n)\\ thin\_heap\_tag&O(1)&均摊\Theta(\log n)& 均摊O(n)&均摊\Theta(\log n)&\Theta(n) \end{array}\)
__gnu_pbds::tree
定义方法
__gnu_pbds::tree<Key,Mapped,Cmp_Fn=std::less<Key>,Tag=rb_tree_tag,Node_Update=null_tree_node_update,Allocator=std::allocator<char> >
- Key:储存的元素类型,如果要储存相同元素,要用
pair
或结构体,并使用lower_bound
或upper_bound
- Mapped:映射规则。若要该树为集合,此处是
null_type
,反之若要该树为map,此处填入value - Cmp_Fn:比较函子
- Tag:选择底层数据结构,
__gnu_pbds
提供三种不同的平衡树rb_tree_tag
:红黑树,一般性能最好splay_tree_tag
:splayov_tree_tag
:由vector
实现
Node_Update
:更新节点的方法,默认null_node_update
,如果要使用order_of_key
或者find_by_order
,需要使用tree_order_statistics_node_update
- Allocator:空间分配器类型
成员函数:
-
insert(x)
:插入元素,返回pair<iterator,bool>
-
erase(x)
:删除元素或迭代器,返回bool表示是否成功 -
order_of_key(x)
:返回x的排名 -
find_by_order(x)
:返回排名所对应的元素的迭代器 -
lower_bound(x)
:做lower_bound,返回迭代器 -
upper_bound(x)
:做upper_bound,返回迭代器 -
join(x)
:将x树并入当前树,删除x -
split(x,b)
:将小于等于x的元素留在当前树,其余的留在b树 -
empty()
:返回是否为空 -
size
:返回大小注意,join应保证键值不相交
模板:普通平衡树
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
#define pii pair<int,int>
#define mk make_pair
tree<pii,null_type,less<pii>,rb_tree_tag,tree_order_statistics_node_update> T;
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
int n;cin>>n;
for(int i = 1;i <= n; ++i){
int op,x;cin>>op>>x;
switch(op){
case 1:T.insert(mk(x,i));break;
case 2:T.erase(T.lower_bound(mk(x,0)));break;
case 3:cout<<T.order_of_key(mk(x,0))+1<<'\n';break;
case 4:cout<<(*T.find_by_order(x-1)).first<<'\n';break;
case 5:cout<<(*--T.lower_bound(mk(x,0))).first<<'\n';break;
case 6:cout<<(*T.upper_bound(mk(x,n))).first<<'\n';break;
}
}
}
rope
需要using namespace __gnu_cxx;
可以实现块状链表,本质是可持久化平衡树,不支持任何与数值有关的操作(如查询排名),不适用于单个字符的修改
\(crope = rope<char>\),可以将其理解为重型string
- 如何可持久化?
rope<int> * f[1000];
f[i] = new rope<int>(*f[i-1]);
时间复杂度\(O(1)\)
-
基本操作
insert(pos,x)
: 在pos添加x,支持插入数组erase(pos,x)
: 在pos后删除x个元素push_back(x)
: 在末尾添加元素substr(pos,x)
: 从pos开始提取xcopy(pos,len,x)
: [pos,len]用x替代
常数有点大,空间复杂度玄学。
可持久化数组板子。
这篇代码只能拿80
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
const int N = 1e6 + 10;
vector<rope<int> > rp(1);
int n,m,a[N];
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
cin>>n>>m;
for(int i = 1,x;i <= n; ++i) cin>>a[i];
rp[0].append(a,n+1);
for(int i = 1;i <= m; ++i){
int ver,op,loc,val;
cin>>ver>>op>>loc;
rp.emplace_back(rp[ver]);
if(op == 1){
cin>>val;
rp.back().replace(loc,val);
}
else{
cout<<rp.back().at(loc)<<'\n';
}
}
}
文艺平衡树
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace __gnu_cxx;
using namespace __gnu_pbds;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll = long long;using ull = unsigned long long;
char *p1,*p2,buf[1<<23];
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
#ifdef linux
#define pc putchar_unlocked
#else
#define pc putchar
#endif
namespace IO{
template<typename T>inline bool read(T &x){x=0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x<<1)+(x<<3)+(s^48);if(!f) x=~x+1;return true;}
inline bool read(double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false; for(;s!=' '&&s!='\n'&&s!=EOF;s=gc())str.push_back(s);return true;}
inline bool read_line(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str.push_back(s);}return true;}
inline bool read_line(char *str){int len=0;char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str[len]=s;len++;}str[len]='\0';return true;}
inline bool read(char &s){char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF||x==' '||x=='\n')return false;s=x;return true;}
inline bool read(char *s){int len=0;char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF)return false;for(;x!=' '&&x!='\n'&&x!=EOF;x=gc())s[len++]=x;s[len]='\0';return true;}
template<class T,class... Args> inline bool read(T &x,Args&... args){return (read(x)&&read(args...));}
template<class T>inline void write(T x){static T st[45];int top=0;if(x<0)x=~x+1,pc('-');do{st[top++]=x%10;}while(x/=10);while(top)pc(st[--top]^48);}
inline void write(char x){pc(x);}
inline void write(string s){for(int i=0;s[i];++i) pc(s[i]);}
inline void write(char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
inline void write(const char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
template<class T,class... Args> inline void write(T x,Args... args){write(x);write(args...);}
}using namespace IO;
rope<int> a,b;
signed main(){
#ifndef ONLINE_JUDGE
infile("in.in");outfile("out.out");
#endif
int n,m;
read(n,m);
for(int i = 0;i < n; ++i){
a.push_back(i+1);
b.push_back(n-i);
}
while(m--){
int l,r;read(l,r);
l--,r--;
rope<int> res = a.substr(l,r-l+1);
a = a.substr(0,l)+b.substr(n-r-1,r-l+1)+a.substr(r+1,n-r-1);
b = b.substr(0,n-r-1)+res+b.substr(n-l,l);
}
for(int i = 0;i < n; ++i) write(a[i],' ');
}
对于数值操作,可以二分插入。
模板是可持久化平衡树。
hash
有两个,分别是使用拉链法的cc_hash_table
,使用探测法的gp_hash_table
。有的博客说探测法容易被卡,但是实测还是探测法跑的更快,所以推荐使用探测法。
定义方法 :
gp/cc_hash_table<typename Key, typename Mapped>
其实后面还有,但是没看懂,OI中一般也不用,所以就省略了。
key
: 是键值。Mapped
: 是实值,如果不想用,那么就在此处填null_type
成员函数 :
[]
: 和map一样。若没有也是加入新的节点。find
: 查找,和map一样。若没有返回end()
STL函数
minmax_element
- 声明 :
template< class ForwardIt, class Compare > std::pair<ForwardIt, ForwardIt> minmax_element( ForwardIt first, ForwardIt last, Compare comp );
默认为小于号
-
比较次数 : 为下界\(\left\lceil\frac{3n}{2}\right\rceil-2\)
-
例题 (我也没想到这玩意还有例题):
点此查看代码
#include<bits/stdc++.h>
#include "ramen.h"
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
void Ramen(int n){
vector<int> a;
for(int i = 0;i < n; ++i) a.push_back(i);
auto ans = minmax_element(a.begin(),a.end(),[](int x,int y){
int p = Compare(x,y);
if(p == -1) return true;
return false;
});
Answer(*ans.first,*ans.second);
}
inplace_merge
-
声明 :
template< class BidirIt, class Compare > void inplace_merge( BidirIt first, BidirIt middle, BidirIt last,Compare comp );
-
作用 : 将\([first,middle)\)和\([middle,last)\)两个已经按照comp排好序的区间按照\(comp\)排序
时间复杂度\(O(n)\)
默认为小于号
includes
-
声明 :
template< class InputIt1, class InputIt2, class Compare > bool includes( InputIt1 first1, InputIt1 last1,InputIt2 first2, InputIt2 last2, Compare comp );
-
作用 : 在有序范围 [first2, last2) 是有序范围 [first1, last1) 的子序列的情况下返回 true(不必是连续的子序列)。
-
比较次数 : \(O(2\times (N_1 + N_2) - 1)\)
默认小于号
STL容器
(先开个坑,留着没事的时候写)
vector
stack
queue
deque
set/unordered_set
mutiset/unordered_mutiset
map/unordered_map
mutimap/unordered_mutimap
priority_queue
string
c++输入,输出,文件操作
seekg
-
作用 : 是对输入文件定位。
-
用法 : 它有两个参数:第一个参数是偏移量,第二个参数是基地址。对于第一个参数,可以是正负数值,正的表示向后偏移,负的表示向前偏移。基地址可以是 :
ios::beg
: 输入流开始的位置ios::end
: 输入流结束的位置ios::cur
: 输入流当前的位置
tellg
- 作用 : 它返回当前定位指针的位置,也代表着输入流的大小
无参数
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18314922