容器
通用操作
O(1) size() ,empty()
O(N) clear()
vector向量——变长数组
头文件:
#include<vector>
创建:
vector
操作:
O(1)
a.empty(); bool
!!! 下标访问 0~ a.size()
a.push_back();
a.pop_back();
a.begin();//头指针,数组非空 *a.begin()=a[0]
a.end(); //尾指针,取尾元素的下一个地址。就相当于左闭右开区间。
两种遍历(只有vector 和 string 可以下标遍历)
vector<int>v;
int main(){
for(int i=5;i;i--)
v.push_back(i*2);
for(vector<int>::iterator it=v.begin();it!=v.end();it++)
printf("%d
",*it);
for(int i=0,siz=v.size();i<siz;i++)
printf("%d
",v[i]);
return 0;
}
O(n)
a.insert()
a.eraser()
比较:
sort(vec.begin(), vec.end(), cmp);
应用
vector 存图
伪平衡树:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
vector <int> v;
int n;
int main(){
n=read();
for(int i=1,opt,x;i<=n;i++){
opt=read();x=read();
if(opt==1) v.insert(lower_bound(v.begin(),v.end(),x),x);
if(opt==2) v.erase(lower_bound(v.begin(),v.end(),x));
if(opt==3) printf("%d
",lower_bound(v.begin(),v.end(),x)-v.begin()+1);
if(opt==4) printf("%d
",v[x-1]);
if(opt==5) printf("%d
",*--lower_bound(v.begin(),v.end(),x));
if(opt==6) printf("%d
",*upper_bound(v.begin(),v.end(),x));
}
return 0;
}
string
头文件:
#include <string>
创建:
string s
操作:
求串的长度:s.size() //不能用strlen
求一个子串:s.substr(开始的位置, 长度) //位置从0开始编号,返回一个string
连接字符串:s = a + b
s1==s2判断是否相等
s1=s2将 s1 赋值为 s2
查找一个字母第一次出现位置:s.find(ch) //返回一个int,从0开始编号
输入输出请使用cin/cout,不能用scanf/printf
cctype中
isalnum(c) 如果c是字母或数字,则为true
isalpha(c) 如果c是字母,则为true
isdigit(c) 如果c是数字,则为true
islower(c) 如果c是小写字母,则为ture
isupper(c) 如果c是大写字母,则为true
isspace(c) 如果c是空白字符,则为true
ispunct(c) 如果c是标点符号,则为true
iscntrl(c) 如果c是控制字符,则为true
isgraph(c) 如果c不是空格,但是可打印,则为true
isprint(c) 如果c是可打印的字符,则为true
isxdigit(c) 如果c是十六进制数,则为true
tolower(c) 如果c是大写字母,则返回其小写字母形式,否则直接返回c
toupper(c) 如果c是小写字母,则返回其大写字母形式,否则直接返回c
s.substr(pos,n) | 返回一个string类型的字符串,它包含s中从下标pos开始的n个字符 |
---|---|
s.substr(pos) | 返回一个string类型的字符串,它包含从下标pos开始到s末为的所有字符 |
s.find(args) 在s中查找args的第一次出现
s.rfind(args) 在s中查找args的最后一次出现
s.find_first_of(args) 在s中查args的任意字符的第一次出现
s.find_last_of(args) 在s中查找args的任意字符的最后一次出现
s.find_first_not_of(args) 在s中查找第一个不属于args的字符
s.find_last_not_of(args) 在s中查找最后一个不属于args的字符
两种遍历
for(it=s.begin();it!=s.end();++it)
cout<<*it<<endl;
for(int i=0;s[i];i++)
cout<<s[i]<<endl;
bitset
状态压缩神器,复杂度: O(n / 32)
#include <bitset>
...
bitset<N> state1, state2;
//初始化:全为0
//state1 : 第0位 ~ 第N - 1位
...
state1 = ~state1;
state1 &= state2;
state1 |= state2;
state1 ^= state2;
state1 <<= 1;
state1 >>= 1;
if (state1 == state2) ...
if (state1 != state2) ...
int d3 = state1[3];
state1[0] = 1;
int tot = state1.count();
deque双端队列
头文件:
#include<deque>
创建:
deque
操作:
a.push_back();
a.push_front();
a.pop_back();
a.pop_front();
应用:
双端bfs ——0 放前面 1放后面
void work(){
deque <node> q;
memset(dis,0x3f,sizeof(dis));
dis[in[s]]=0;q.push_front({in[s],0});
while(!q.empty()){
int x=q.front().id;q.pop_front();
if(vis[x])continue;
vis[x]=1;
for(int i=hd[x];i;i=e[i].nxt){
int y=e[i].to,z=e[i].w;
if(dis[y]>dis[x]+z){
dis[y]=dis[x]+z;
if(!vis[y]){
if(!z) q.push_front({y,dis[y]});
else q.push_back({y,dis[y]});
}
}
}
}
}
list双向链表
按顺序走到需存取的元素,O(n),在任何位置上执行插入或删除动作O(1)
头文件:
#include<list>
创建:
操作:
a.push_back();
a.push_front();
a.pop_back();
a.pop_front();
a.begin();//头指针
a.end(); //尾指针
a.erase(it) //将it位置删除
a.insert(it,elem);
a.insert(l1.begin(),l2.begin(),l2.end());在l1开始位置插入l2的从开始到结束的所有位置的元素
a.empty();
a.front() a.back()
a.merge():合并两个链表并使之默认升序
而 list 的迭代器是双向迭代器,所以以下代码可以:
list<int> v;
list<int>::const_iterator ii;
for( ii = v.begin(); ii != v.end ();ii ++ )
cout << * ii;
以下代码则不行:
for( ii = v.begin(); ii < v.end ();ii ++ )
cout << * ii;
//双向迭代器不支持 <
for(int i = 0;i < v.size() ; i ++)
cout << v[i]; //双向迭代器不支持 []
pair
pair的实现是一种结构体,主要的两个成员变量是first, second
头文件:
#include<utility>
创建:
$ pair<T1, T2> p; $
//创建一个空的pair对象,它的两个元素分别是T1和T2类型 ——可以是int ,double ,char ,string.....
应用:
图论里有边权
$ vector< pair<int,int> > g[N] $
g[x].push_back(make_pair(y,z))
$ y=g[x][i].first ,w=g[x][i].second $
或者代替二元结构体
诡异离散化
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=4e6+10;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,cnt;
int b[N],c[N];
pair<int,int>a[N];
void update(int x){
for(;x<=N;x+=x&(-x))
c[x]++;
}
int query(int x){
int res=0;
for(;x;x-=x&(-x))
res+=c[x];
return res;
}
int ans;
bool cmp(pair<int,int> a,pair<int,int> b){
return a.second<b.second;
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=make_pair(read(),i);
sort(a+1,a+1+n);
for(int i=1;i<=n;i++) a[i].first=i;
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
update(a[i].first);
ans=max(ans,i-query(a[i].first));
}
printf("%d
",ans+1);
return 0;
}
比较:
p1 < p2; 先比first,再比second
p1 == p2; first和second依次相等
排序
sort(a,a+n); //默认对Item的first的值进行排序
自定义的sort()函数
bool cmp(pair<int, int>a, pair<int, int>b){
return a.first<b.first; //根据fisrt的值升序排序
}
bool cmp(pair<int, int>a, pair<int, int>b){
return a.first>b.first; //根据second的值升序排序
}
Map/Multimap映射
Map的元素是成对的键值/实值,内部的元素依据其值自动排序,Map内的相同数值的元素只能出现一次,Multimaps内可包含多个数值相同的元素,内部由二叉树实现,便于查找;O(logn)
头文件:
#include<map>
创建:
map<类型1,类型2>变量名
操作:
map的两种访问方式:下标访问、迭代器访问
mp[“abc”]=2;
mp.find()返回值是一个迭代器,成功返回迭代器指向要查找的元素,失败返回的迭代器指向end。*
mp.count()方法返回值是一个整数,1表示有这个元素,0表示没有这个元素————BSGS里用到
应用:
字符串对应
CF650A Watchmen
注意去重
queue队列
priority_queue优先队列,大根堆
堆的应用戳这里
先进先出
头文件:
#include<queue>
操作:
a.push();//优先队列是O(logN)
a.empty();
a.front(); //优先队列是 a.top() O(1)
a.back();
a.pop(); //优先队列O(logN)
应用:
priority_queue<int,vector<int>,greater<int>> pq;
//优先队列总是把最小的元素放在队首。
priority_queueK<int,vector<int>,less<int>> pq;
//优先队列总是把最大的元素放在队首。
stack栈
后进先出
头文件:
#include<stack>
创建:
stack
操作:
a.push();
a.top();
a.pop();
应用:
用来模拟实现递归。程序的栈内存空间很小,如果用普通递归,可能会导致程序运行崩溃。
用栈模拟递归可以避免这种问题。
set集合
set内的元素,自动递增排序,并且去重。
头文件:
#include<set>
创建:
操作:
for(set<int>::iterator it=st.begin();it!=st.end();it++) {
printf("%d ",*it);
}
O(logN):
insert()
find()
erase()
erase(a,b);删除左闭右开区间内[a,b)的元素,时间复杂度为O(b-a)
应用:
查询前驱后继:
set<int>::iterator l=--s.lower_bound(x); set<int>::iterator r=s.lower_bound(x);
cout<<*l<<“ ”<<*r<<endl;
查找一个元素是否在set里:
it = s.find(x);
如果有x,返回其迭代器,否则返回s.end();
set<int>s;
int main(){
s.insert(N);s.insert(-N);
for(int i=5;i;i--)
s.insert(i*2);
set<int>::iterator it;
for(int i=1;i<=10;i++) {
it=s.find(i);
if(it==s.end()) puts("NO");
else printf("%d
",*it);
}
return 0;
}
注意开long long
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<set>
#include <limits.h>
using namespace std;
typedef long long ll;
set<pair<int,int>> s;
int main(){
int n;
cin>>n;
s.insert({-0x7fffffff,0});
s.insert({0x7fffffff,0});
for(int i=1;i<=n;i++){
int v;
cin>>v;
if(i>1){
auto it=s.upper_bound({v,0});//任意位置,所以可以为0(只是用v的值来查找)
auto jt=it;
jt--;
ll ans1=(ll)v-jt->first,ans2=it->first-(ll)v;
if(ans1<=ans2) cout<<ans1<<" "<<jt->second<<endl;
else cout<<ans2<<" "<<it->second<<endl;
}
s.insert({v,i});
}
}