乱七八糟
IO优化
//适用于非负整数
template<class T>
void read(T&ret) {
char c;
ret=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9')ret=ret*10+(c-'0'),c=getchar();
}
//适用于整数
template<class T>
bool read(T&ret) {
char c;
int sgn;
if(c=getchar(),c==EOF)return 0;//EOF
while(c!='-'&&(c<'0'||c>'9'))c=getchar();
sgn=(c=='-')?-1:1;
ret=(c=='-')?0:(c-'0');
while(c=getchar(),c>='0'&&c<='9')ret=ret*10+(c-'0');
ret*=sgn;
return 1;
}
//适用于整数,(int,long long,float,double)
template<class T>
bool read(T&ret) {
char c;
int sgn;
T bit=0.1;
if(c=getchar(),c==EOF)return 0;
while(c!='-'&&c!='.'&&(c<'0'||c>'9'))c=getchar();
sgn=(c=='-')?-1:1;
ret=(c=='-')?0:(c-'0');
while(c=getchar(),c>='0'&&c<='9')ret=ret*10+(c-'0');
if(c==' '||c=='\n') {
ret*=sgn;
return 1;
}
while(c=getchar(),c>='0'&&c<='9')ret+=(c-'0')*bit,bit/=10;
ret*=sgn;
return 1;
}
//输出外挂
void out(int x) {
if(x>9)out(x/10);
putchar(x%10+'0');
}
ios::sync_with_stdio(0),cin.tie(0);
cout<<fixed<<setprecision(2)<<3.0<<endl;
cout<<setfill('0')<<setw(2)<<3<<endl;
优先级
优先级 |
运算符 |
1 |
[] () . -> ++ -- |
2 |
- (类型) ++ -- * & ! ~ sizeof |
3 |
/ * % |
4 |
+ - |
5 |
<< >> |
6 |
> >= < <= |
7 |
== != |
8 |
& |
9 |
^ |
10 |
| |
11 |
&& |
12 |
|| |
13 |
?: |
14 |
= /= *= %= += -= <<= >>= &= ^= |= |
15 |
, |
string
getline(cin,str);//带空格输入字符串
str.substr(p0,len);//其中len可以不填,默认取到末尾。str.erase(p0
,len);
str.erase(str.begin()+i);//删除第i个str1.insert(p0,str2
,pos,len);//后两个参数截取str2,可以省略
s.insert(int p0,int n,char c);//在p0处插入n个字符cs1.replace(p0,len0,str2
,pos,len);//删除p0开始的len0个字符,然后在p0处插入串str2中从pos开始的len个字符str1.find(str2
,pos)//从前往后,查找成功时返回所在位置,失败返回string::npos的值 (-1)
str1.rfind(str2,pos)//从pos开始从后向前查找字符串str2中字符串在当前串中的位置
vector
v.front() // 传回第一个数据。
v.back() // 传回最后一个数据,不检查这个数据是否存在。
v.erase(pos) // 删除pos位置的数据,传回下一个数据的位置。
v.erase(beg,end) //删除[beg,end)区间的数据,传回下一个数据的位置。
v.insert(pos,elem) // 在pos位置插入一个elem拷贝,传回新数据位置。
v.insert(pos,n,elem) // 在pos位置插入n个elem数据。无返回值。
v.insert(pos,beg,end) // 在pos位置插入在[beg,end)区间的数据。无返回值。
priority_queue
priority_queue<int>q;//top是最大值
priority_queue<int,vector<int>,greater<int> >q;//top是最小值
bool operator<(Node a,Node b){return a.x<b.x;}//放到结构体里面时需要加const
struct cmp{bool operator()(int a,int b){return a<b;}};
priority_queue<int,vector<int>,cmp_key>q;
rope
#include <ext/rope>//可持久化平衡树
using namespace __gnu_cxx;
rope<int>r;
r.push_back(x);//在末尾添加x
r.insert(pos, x); //在pos插入x
r.erase(pos, x); //从pos开始删除x个
r.copy(pos, len, x); //从pos开始到pos+len为止用x代替
r.replace(pos, x); //从pos开始换成x
r.substr(pos, x); //提取pos开始x个
r[x]; //访问第x个元素
bitset
bitset<5>b;//坐标从后往前计数,高位在前
bitset<5>b(13);
bitset<5>b("1101");
b.count();//count函数用来求bitset中1的位数,一共3
b.size();//size函数用来求bitset的大小,一共5
b.any();//any函数检查bitset中是否有1
b.none();//none函数检查bitset中是否没有1
b.all();//all函数检查bitset中是全部为1
foo.flip();//flip函数不指定参数时,将bitset每一位全部取反
foo.set();//set函数不指定参数时,将bitset的每一位全部置为1
foo.reset();//reset函数不传参数时将bitset的每一位全部置为0
string s = foo.to_string();//将bitset转换成string类型
unsigned long a = foo.to_ulong();//将bitset转换成unsigned long类型
unsigned long long b = foo.to_ullong();//将bitset转换成unsigned long long类型
rand
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
double rnd(double l, double r){return uniform_real_distribution<double>(l,r)(rng);}
int rnd(int l,int r){return uniform_int_distribution<int>(l,r)(rng);}
数据结构
主席树
//本模板是离散后对权值建树
#include<bits/stdc++.h>
#define mid (l+r>>1)
using namespace std;
const int N=2e5+10;
struct TR
{
int sum,lo,ro;
}tr[N<<5];
int tr_cnt;//之后需要初始化=0
int n,m,q,arr[N],brr[N],rt[N];//m是权值的数量
void build(int &o,int l=1,int r=m)
{
o=++tr_cnt;
//tr[o].sum=0;
if(l==r)return;
build(tr[o].lo,l,mid);
build(tr[o].ro,mid+1,r);
}
void update(int p,int v,int pre,int &o,int l=1,int r=m)
{
o=++tr_cnt;
tr[o]=tr[pre];
tr[o].sum+=v;//都是+1
if(l==r)return;
if(p<=mid)update(p,v,tr[pre].lo,tr[o].lo,l,mid);
else update(p,v,tr[pre].ro,tr[o].ro,mid+1,r);
}
//u和v是两个线段树的根,相减后的线段树求第k个的下标位置
int query(int k,int u,int v,int l=1,int r=m)
{
if(l==r)return l;
int tmp=tr[tr[v].lo].sum-tr[tr[u].lo].sum;
if(tmp>=k)return query(k,tr[u].lo,tr[v].lo,l,mid);
else return query(k-tmp,tr[u].ro,tr[v].ro,mid+1,r);
}
int main()
{
int n,q;scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%d",arr+i),brr[i]=arr[i];
sort(brr+1,brr+1+n);
m=unique(brr+1,brr+1+n)-brr-1;
build(rt[0]);
for(int i=1;i<=n;i++)
{
int p=lower_bound(brr+1,brr+1+m,arr[i])-brr;
update(p,1,rt[i-1],rt[i]);
}
while(q--)
{
int u,v,k;
scanf("%d%d%d",&u,&v,&k);
printf("%d\n",brr[query(k,rt[u-1],rt[v])]);
}
return 0;
}
树状数组
namespace BTree
{
const int N = 5e5 + 10;
int n, c[N];
int lb(int a) {return a & (-a);}
void add(int p, int v)
{
for(; p <= n; p += lb(p))
c[p] += v;
}
int query(int p)
{
int ret = 0;
for(; p; p -= lb(p))
ret += c[p];
return ret;
}
};
RMQ
//O(20*n)复杂度,O(1)求1~n的最大最小值
//ma[j][i]代表i~i+(1<<j)-1的最值
struct RMQ
{
static const int N=1e5+10;
int n,ma[20][N],lg[N];
void build(int n){
lg[0]=-1;
for(int i=1;i<=n;i++){
lg[i]=lg[i-1]+(i&i-1?0:1);
ma[0][i]=arr[i].se;
}
for(int j=1;j<=lg[n];j++)for(int i=1;i<=n-(1<<j)+1;i++)
ma[j][i]=max(ma[j-1][i], ma[j-1][i+(1<<j-1)]);
}
int query(int l, int r){
int k=lg[r-l+1];
return max(ma[k][l], ma[k][r-(1<<k)+1]);
}
}sw;
线段树合并/分裂
#include <bits/stdc++.h>
#define mid (l+r>>1)
#define ll long long
#define endl '\n'
using namespace std;
const int N = 2e5 + 10;
int n,m,cnt,seq = 1,bac[N<<5],lo[N<<5],ro[N<<5],rt[N];
ll val[N << 5];
int newnod () {return bac[cnt--];}//从库中取出某下标
void del (int o) {bac[++cnt] = o, lo[o] = ro[o] = val[o] = 0;}//把下标回收
void add (int p, int v, int &o, int l = 1, int r = n)
{//单点加操作
if (!o)o = newnod();
val[o] += v;
if (l == r)return;
if (p <= mid)add(p, v, lo[o], l, mid);
else add(p, v, ro[o], mid + 1, r);
}
ll query (int ql, int qr, int o, int l = 1, int r = n)
{//查询区间和
if (ql <= l && r <= qr)return val[o];
ll ret = 0;
if(ql <= mid)ret = query(ql, qr, lo[o], l, mid);
if(qr > mid)ret += query(ql, qr, ro[o], mid + 1, r);
return ret;
}
int kth (int k, int o, int l = 1, int r = n)
{//查询o处线段树第k小,本题不用离散化
if (l == r)return l;
if (val[lo[o]] >= k)return kth(k, lo[o], l, mid);
else return kth(k - val[lo[o]], ro[o], mid + 1, r);
}
int merge (int x, int y)
{//合并下标x和y节点处的线段树,返回合并后的下标
if (!x || !y)return x+y;
val[x] += val[y];
lo[x] = merge(lo[x], lo[y]);
ro[x] = merge(ro[x], ro[y]);
del(y);
return x;
}
void split (int x, int &y, ll k)
{//把x的前k个保留,剩下的分给y
if (!x)return;
y = newnod();
ll v = val[lo[x]];
if (k > v)split(ro[x], ro[y], k - v);
else swap(ro[x], ro[y]);
if (k < v)split(lo[x], lo[y], k);
val[y]=val[x]-k,val[x]=k;
return;
}
int main ()
{
for(int i = 1; i < (N << 5); i++)bac[++cnt] = i;//把下标存进库中
cin >> n >> m;
for (int i = 1,a; i <= n; i++){cin >> a;add(i, a, rt[1]);}
for (int i = 1,op,p,x,l,r,q,t,k; i <= m; i++)
{
cin >> op;
if (op == 0)
{//将可重集p中大于等于l且小于等于r的值放入一个新的可重集中
cin >> p >> l >> r;
ll k1 = query(1, r, rt[p]), k2 = query(l, r, rt[p]);
int tmp;
split(rt[p], rt[++seq], k1 - k2);//先把l~n分裂给新集合
split(rt[seq], tmp, k2);//再把r~n分裂出来给tmp
rt[p] = merge(rt[p], tmp);//把tmp还给p
}
else if (op == 1)
{//将可重集 t 中的数放入可重集 p,且清空可重集 t
cin >> p >> t;
rt[p] = merge(rt[p], rt[t]);
}
else if (op == 2)
{//在 p 这个可重集中加入 x 个数字 q
cin >> p >> x >> q;
add(q, x, rt[p]);
}
else if (op == 3)
{//查询可重集 p 中大于等于 x 且小于等于 y 的值的个数。
cin >> p >> l >> r;
cout << query(l, r, rt[p]) << endl;
}
else if (op == 4)
{//查询在 p 这个可重集中第 k 小的数,不存在时输出 -1
cin >> p >> k;
cout<<(val[rt[p]] < k?-1:kth(k, rt[p]))<<endl;
}
}
}
平衡树
#include<bits/stdc++.h>
const int N = 1e5+5;
using namespace std;
int n;
int tot,root;
int w[N],num[N],sz[N],fa[N],son[N][2];
void update(int x) {
sz[x]=sz[son[x][0]]+sz[son[x][1]]+num[x];
}
//void pushdown(int x) {
// do something...
//}
void rotate(int x) {
// pushdown(fa[x]);pushdown(x);
int y=fa[x],z=fa[y],t=(son[y][0]==x);
fa[y]=x;fa[x]=z;
if(z) son[z][son[z][1]==y]=x;
son[y][!t]=son[x][t];fa[son[x][t]]=y;
son[x][t]=y;
update(y);update(x);
}
void splay(int x,int f) {
// pushdown(x);
while(fa[x]!=f) {
int y=fa[x],z=fa[y];
if(z!=f) {
if(son[y][0]==x^son[z][0]==y)rotate(x);
else rotate(y);
}
rotate(x);
}
if(!f)root=x;
}
void insert(int val,int &x=root,int f=0) {
if(!x) {
x=++tot;fa[x]=f;
son[x][0]=son[x][1]=0;
w[x]=val;sz[x]=num[x]=1;
splay(x,0);
return;
}
if(val==w[x]) {
sz[x]++;num[x]++;
splay(x,0);
return;
}
insert(val,son[x][val>w[x]],x);
update(x);
}
int get(int val) {
int x=root;
// pushdown(x);
while(x&&w[x]!=val) {
x=son[x][val>w[x]];
// pushdown(x);
}
return x;
}
void delet(int w) {
int x=get(w);
if(!x)return;
splay(x,0);
if(num[x]>1) {
num[x]--;sz[x]--;
return;
}
if(!son[x][0]||!son[x][1])root=son[x][0]+son[x][1];
else {
int y=son[x][1];
while(son[y][0])y=son[y][0];
splay(y,x);
fa[son[x][0]]=y;
son[y][0]=son[x][0];
root=y;
son[x][0]=son[x][1]=0;
}
fa[root]=0;
update(root);
}
int getrank(int val) {
int x=root,ret=0,last=0;
while(x) {
last=x;
if(val>w[x]) {
ret+=sz[son[x][0]]+num[x];
x=son[x][1];
} else if(val==w[x]) {
ret+=sz[son[x][0]];
break;
} else {
x=son[x][0];
}
}
if(last)splay(last,0);
return ret+1;
}
int kth(int k) {
int x=root;
// pushdown(x);
while(k<=sz[son[x][0]]||k>sz[son[x][0]]+num[x]) {
if(k<=sz[son[x][0]])x=son[x][0];
else k-=sz[son[x][0]]+num[x],x=son[x][1];
// pushdown(x);
}
return w[x];
}
int getpre(int val) {
int x=root,ret=0,last=0;
while(x) {
last=x;
if(val>w[x]) {
ret=w[x];
x=son[x][1];
} else {
x=son[x][0];
}
}
if(last)splay(last,0);
return ret;
}
int getne(int val) {
int x=root,ret=0,last=0;
while(x) {
last=x;
if(val<w[x]) {
ret=w[x];
x=son[x][0];
} else {
x=son[x][1];
}
}
if(last)splay(last,0);
return ret;
}
int main() {
scanf("%d",&n);
for(int i=1,x,y; i<=n; ++i) {
scanf("%d%d",&x,&y);
if(x==1)insert(y);
if(x==2)delet(y);
if(x==3)printf("%d\n",getrank(y));
if(x==4)printf("%d\n",kth(y));
if(x==5)printf("%d\n",getpre(y));
if(x==6)printf("%d\n",getne(y));
}
}
整体二分
#include<bits/stdc++.h>
#define mid (l+r>>1)
using namespace std;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
int n,m,cnt,ans[N];
struct A
{
int l,r,k,id,op;
}q[N<<1],q1[N<<1],q2[N<<1];
int c[N];
int lb(int a){return a&-a;}
void add(int p,int v)
{
for(;p<=n;p+=lb(p))
c[p]+=v;
}
int query(int p)
{
int ret=0;
for(;p;p-=lb(p))
ret+=c[p];
return ret;
}
void solve(int l,int r,int ql,int qr)
{
if(ql>qr)return;
if(l==r)
{
for(int i=ql;i<=qr;i++)if(q[i].op==2)
ans[q[i].id]=l;
return;
}
int cnt1=0,cnt2=0;
for(int i=ql;i<=qr;i++)
{
if(q[i].op==1)
{
if(q[i].l<=mid)q1[++cnt1]=q[i],add(q[i].id,1);
else q2[++cnt2]=q[i];
}
else
{
int t=query(q[i].r)-query(q[i].l-1);
if(q[i].k<=t) q1[++cnt1]=q[i];
else q[i].k-=t,q2[++cnt2]=q[i];
}
}
for(int i=1;i<=cnt1;i++)if(q1[i].op==1)add(q1[i].id,-1);
for(int i=1;i<=cnt1;i++)q[i+ql-1]=q1[i];
for(int i=1;i<=cnt2;i++)q[ql+cnt1+i-1]=q2[i];
solve(l,mid,ql,ql+cnt1-1);
solve(mid+1,r,ql+cnt1,qr);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cnt++;
cin>>q[cnt].l;
q[cnt].id=i,q[cnt].op=1;
}
for(int i=1;i<=m;i++)
{
cnt++;
cin>>q[cnt].l>>q[cnt].r>>q[cnt].k;
q[cnt].id=i,q[cnt].op=2;
}
solve(-inf,inf,1,cnt);
for(int i=1;i<=m;i++)cout<<ans[i]<<endl;
return 0;
}
可撤销并查集
struct UFS {
struct A{int tp,id,w;};
stack<A> stk;
vector<int>fa,rnk;
UFS(int n) {
fa.assign(n+1,0);
rnk.assign(n+1,0);
for (int i = 0; i <= n; ++i) fa[i] = i;
}
int Find(int x) {
while(x^fa[x]) x = fa[x];
return x;
}
int Merge(int x, int y) { //返回本次合并栈的增加数量
int ret=0;
x = Find(x), y = Find(y);
if(x == y) return 0;
ans++;
if(rnk[x] <= rnk[y]) {
stk.push({1, x, fa[x]});ret++;
fa[x] = y;
if(rnk[x] == rnk[y]) {
stk.push({2, y, rnk[y]});ret++;
rnk[y]++;
}
}
else {
stk.push({1, y, fa[y]});ret++;
fa[y] = x;
}
return ret;
}
void Undo() {
A a=stk.top();stk.pop();
if(a.tp==1)fa[a.id]=a.w,ans--;
else rnk[a.id]=a.w;
}
};
图论
最小生成树
/*
* Prim 求无向图最小生成树
* 无堆优化O(n*n),空间O(n*n),适用于稠密图
* 耗费矩阵 cost[N][N],标号从 0 开始,0~n-1
* 矩阵cost[N][N],初始化为inf,注意重边问题
* 返回最小生成树的权值,返回 -1 表示原图不连通
*/
bool vis[N];
int n, lowc[N], cost[N][N];
//点是 0 n-1
int Prim()
{
int ret = 0;
memset(vis, false, sizeof(*vis)*n);
vis[0] = true;
for(int i = 1; i < n; i++)
lowc[i] = cost[0][i];
for(int i = 1; i < n; i++)
{
int mi = inf;
int id = -1;
for(int j = 0; j < n; j++)
if(!vis[j] && mi > lowc[j])
{
mi = lowc[j];
id = j;
}
if(mi == inf)
return -1;//原图不连通
ret += mi;
vis[id] = true;
for(int j = 0; j < n; j++)
if(!vis[j] && lowc[j] > cost[id][j])
lowc[j] = cost[id][j];
}
return ret;
}
/*
* Kruskal 算法求 MST
* 适用于稀疏图,O(Elog(E))
*/
tri edg[M];//普通存边(非前向星)
int edg_cnt;//记得初始化为 0
void addedg(int a, int b, int c) {edg[edg_cnt++] = {a, b, c};}
int father[N];//并查集
int ff(int a) {return father[a] == a ? a : father[a] = ff(father[a]);}
//传入点数,返回最小生成树的权值,如果不连通返回 -1
int Kruskal()
{
for(int i = 1; i <= n; i++)
father[i] = i;
sort(edg, edg + edg_cnt, [](tri a, tri b) {return a.c < b.c;});
int cnt = 0, ret = 0; //计算加入的边数
for(int i = 0; i < edg_cnt; i++)
{
int fa = ff(edg[i].a), fb = ff(edg[i].b);
if(fa != fb)
{
ret += edg[i].c;
father[fa] = fb;
cnt++;
}
if(cnt == n - 1)
return ret;
}
return -1;
}
最短路
struct Dij
{
int n,s;
vector<bool>vis;
vector<ll>dis;
vector<vector<pair<int,ll> > >edg;
Dij(int n,int s=0)
{
this->n=n;
this->s=s;
vis.assign(n,0);
dis.assign(n,linf);
edg.assign(n,{});
}
void add_edg(int u,int v,int w){edg[u].push_back({v,w});};
void solve()
{
dis[s]=0;
priority_queue<pair<ll,int>>q;
q.push({0,s});
while(!q.empty())
{
int u=q.top().se;q.pop();
if(vis[u])continue;
vis[u]=1;
for(auto [v,w]:edg[u])if(!vis[v]&&dis[u]+w<dis[v])
{
dis[v]=dis[u]+w;
q.push({-dis[v],v});
}
}
}
};
struct SPFA
{
vector<bool>vis;vector<ll>dis;vector<int>cnt;
vector<vector<pair<int,ll>>>edg;
int n,s;
SPFA(int n,int s=1)
{
this->n=n;this->s=s;
vis.assign(n+1,0);
dis.assign(n+1,inf);
edg.assign(n+1,{});
cnt.assign(n+1,0);
}
void add_edg(int u,int v,ll w){edg[u].push_back({v,w});}
bool solve()
{
dis[s]=0;
queue<int>q;
q.push(s);