缺省源和模板
整这个玩意单纯只是怕打比赛时要粘板子发现找不到了。
UPD:2022/11/1 加入 math
模块中,排列组合数的边界判断,以及卢卡斯定理求大数组合数。
UPD:2022/11/6 加入了 modint
类型的 ^,^=,<
等运算。
修改了部分代码。
UPD:2022/11/9 加入 Debug
中,记录程序运行时间的语句。
UPD:2022/11/10 更换了更快的 快速乘,不基于 unsigned long long
的自然溢出,具体参考了这篇 blog。
略微改动 modint
部分,使之支持对题目给定模数取模。
UPD:2022/11/13 更改了整个 Debug
模块,并 加入记录程序使用空间的语句。
缺省源
先是头文件和一堆宏定义,一般都是带着的。
code
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using ld=long double;
using ull=unsigned long long;
#define lc p<<1
#define rc p<<1|1
#define lb(x) (x&-x)
#define pb push_back
#define ch(i) (i-'a')
#define vi vector<int>
#define F(i) (i).first
#define S(i) (i).second
#define X(i) (i>n?i-n:i)
#define pi pair<int,int>
#define id(x,y) ((x-1)*m+y)
#define cl(x) ((x).clear());
#define si(i) ((int)i.size())
#define kw(x,k) (((x)>>(k))&1)
#define all(x) (x).begin(),(x).end()
#define me(t,x) memset(t,x,sizeof(t))
#define L(i,j,k) for(int (i)=(j);i<=(k);(i)++)
#define R(i,j,k) for(int (i)=(j);i>=(k);(i)--)
#define dout(a,x) cout<<setprecision(x)<<a<<'\n';
#define ll(i,j,k,l) for(int (i)=(j);i<=(k);(i)+=(l))
#define rr(i,j,k,l) for(int (i)=(j);i>=(k);(i)-=(l))
#define FST ios::sync_with_stdio(false);cin.tie(nullptr);
再是用来Debug的一堆宏定义。
提交时把 #define DG
注释掉就行了。
如果是在 Luogu,要把 #pragma GCC optimize(2)
删掉。
就不用挨个调试代码去删了。
在调时也减少了弄混变量数组的情况。
PS:现在暂时不支持直接输出整个数组和容器。将来学会之后或许会加上。
tam
是用于测程序运行时间和空间的。
在 #define DE
的情况下,在程序末尾加上 return tam;
,打印运行时间和使用空间。
上交时可以不注释掉(但本地会照常输出)。
测空间要定义两个 char
变量,分别在开头和结尾(反正就是把开的数组夹在中间)。
code
#define DE
#pragma GCC optimize(2)
template<typename ...Args>
void W(string X,Args&&...V){cout<<X<<"]=[";string D=""; (...,(cout<<D<<V,D=","));cout<<"]\n";}
#ifdef DE
#define tam cerr<<1e3*clock()/CLOCKS_PER_SEC<<" ms "<<(&y_y-&x_x)/1024.0/1024.0<<" MB\n",0
#define dg(...) cerr<<__func__<<":"<<__LINE__<<" [",W(#__VA_ARGS__,__VA_ARGS__)
#else
#define dg(...)
#define tam 0
#endif
随机数生成器和序列随机打乱函数,用的是 std::mt19937
。
需要 C++11
及以上版本。
分别是生成 范围内的整数和实数。
根据测试,在开启 O2 优化时,1s 大约可以跑 次整数生成或 次实数生成。
时间复杂度近似 。
其实是打对拍时用的。
code
mt19937 mrand(chrono::system_clock::now().time_since_epoch().count());
int rnd(int l,int r){
uniform_int_distribution<>dist(l,r);
return dist(mrand);
}double rnd(double l,double r){
uniform_real_distribution<>dist(l,r);
return dist(mrand);
}void dl(vi a){shuffle(all(a),mrand);}
连续读入,输出,没啥用,但还存上了。
code
namespace IO{
template<typename _Tp>void rd(_Tp &x){cin>>x;}
template<typename _Tp,typename ...Args>void rd(_Tp &x,Args &...args){rd(x),rd(args...);}
template<typename _Tp>void wr(_Tp x){cout<<x;}
template<typename _Tp,typename ...Args>void wr(_Tp x,Args ...args){wr(x),wr(args...);}
}using namespace IO;
//rd(x,y,z);
//wr(x,' ',y,'\n');
namespace FT{
void rd(pi &x){IO::rd(F(x),S(x));}
void rd(vi &v,int n){v.resize(n);for(int &p:v) IO::rd(p);}
void wr(pi x,char c){IO::wr(F(x),' ',S(x),c);}
void wr(vi v,char c){for(int p:v) IO::wr(p,c);}
}using namespace FT;
之后是一个 modint
类的封装。
改编自 的某个提交中部分代码。
就按照普通变量类型定义即可,注意没有取模操作,不然 CE。
相同情况下常数较大。
如果固定模数,就直接对 mod
变量赋值,否则就读入 mod
变量。
后面的 math
模块是时间复杂度 的。
没有直接只是预处理阶乘数组然后直接除是因为这样时间复杂度 ,且常数较大。
code
int mod;
#define P ((int)mod)
int O(int x){return x<0?(x+=P):x<P?x:(x-=P);}
template<class T>
T ksm(T a,ll b){T s=1;for(;b;b>>=1,a*=a) if(b&1) s*=a;return s;}
struct Z{
int x;Z(int x=0):x(O(x)){}Z(ll x):x(O(x%P)){}
bool operator<(const Z&b)const{return x<b.x;}
bool operator>(const Z&b)const{return x>b.x;}
bool operator<=(const Z&b)const{return x<=b.x;}
bool operator>=(const Z&b)const{return x>=b.x;}
bool operator==(const Z&b)const{return x==b.x;}
bool operator!=(const Z&b)const{return x!=b.x;}
int val()const{return x;}bool operator!(){return !x;}
Z operator-()const{return Z(O(P-x));}
Z inv()const{assert(x!=0);return ksm(*this,P-2);}
Z &operator^=(int b){
Z a=*this,c=1;for(;b;b>>=1,a*=a) if(b&1) c*=a;
return x=c.x,*this;
}Z&operator++(){return x=O(x+1),*this;}
Z&operator--(){return x=O(x-1),*this;}
Z&operator*=(const Z&r){x=(ll)(x)*r.x%P;return*this;}
Z&operator+=(const Z&r){x=O(x+r.x);return*this;}
Z&operator-=(const Z&r){x=O(x-r.x);return*this;}
Z&operator/=(const Z&r){return*this*=r.inv();}
Z&operator+=(const int &r){x=O(x+r);return *this;}
Z&operator-=(const int &r){x=O(x-r);return *this;}
Z&operator*=(const int &r){x=(ll)(x)*r%P;return *this;}
Z&operator/=(const int &r){Z x=r;return *this*=x.inv();}
friend Z operator*(const Z&l,const Z&r){Z s=l;s*=r;return s;}
friend Z operator+(const Z&l,const Z&r){Z s=l;s+=r;return s;}
friend Z operator-(const Z&l,const Z&r){Z s=l;s-=r;return s;}
friend Z operator/(const Z&l,const Z&r){Z s=l;s/=r;return s;}
friend Z operator*(const Z&l,const int&r){Z s=l;s*=r;return s;}
friend Z operator+(const Z&l,const int&r){Z s=l;s+=r;return s;}
friend Z operator-(const Z&l,const int&r){Z s=l;s-=r;return s;}
friend Z operator/(const Z&l,const int&r){Z s=l;s/=r;return s;}
friend Z operator^(Z a,int b){return a^=b;}
friend istream&operator>>(istream&is,Z&a){ll v;is>>v;a=Z(v);return is;}
friend ostream&operator<<(ostream&os,const Z&a){return os<<a.val();}
};namespace math{
Z jc[1000100],ijc[1000100];
void init(int x){
jc[0]=1;L(i,1,x) jc[i]=jc[i-1]*i;
ijc[x]=jc[x].inv();R(i,x,1) ijc[i-1]=ijc[i]*i;
}Z C(int n,int m){return m<0||n<m?0:jc[n]*ijc[n-m]*ijc[m];}
Z A(int n,int m){return m<0||n<m?0:jc[n]*ijc[n-m];}
Z BC(int n,int m){return m?BC(n/P,m/P)*C(n%P,m%P):1;}
}using namespace math;
模板
线性筛质数
用的是 埃氏筛+ std::bitset
。
有必要的话把那个 std::vector<int>
加上,存储范围内的质数。
时间复杂度 ,但是实际上跑的比欧拉筛要快(无论欧拉筛是否有 std::bitset
的优化)。
code
bitset<20000000>isp;vi pr;
void es(int n){
isp.set();isp[0]=isp[1]=0;
L(i,2,sqrt(n)) if(isp[i])
ll(j,(i<<1),n,i) isp[j]=0;
// L(i,2,n) if(isp[i]) pr.pb(i);
}
素数检测
用的是 Miller Rabin 质数检测 ,在 long long
范围内应该不会出现错误。
为此还专门加了一个快速幂和快速乘。
时间复杂度 long long
: int
:
code
ll qmul(ll a,ll b,ll p) {
ll rt=a*b-((ll)((ld)a/p*b))*p;
return rt>=p?rt-p:rt<0?rt+p:rt;
}ll power(ll a,ll b,ll p){
ll s=1;while(b){
if(b&1) s=qmul(s,a,p);
a=qmul(a,a,p);b>>=1;
}return s;
}bool mr(ll n){
if(n<3||n%2==0) return n==2;
ll u=n-1,t=0;while(!(u&1)) u>>=1,++t;
vi ud;if(n<=2147483647) ud={2,7,61};
else ud={2,325,9375,28178,450775,9780504,1795265022};
for(ll a:ud){
ll v=power(a,u,n);
if(v==1||v==n-1||v==0) continue;
L(j,1,t){
v=qmul(v,v,n);
if(v==n-1&&j!=t){v=1;break;}
if(v==1) return 0;
}if(v^1) return 0;
}return 1;
}
网络流
费用流暂时没有整理,最大流用的是 ,也把边封装进去,注意每次跑时的清空。
code
struct flow{
int st,ed,T,a[N];
struct E{int v,w,nt;}e[N<<5];
int fir[N],c=1;
void I(int u,int v,int w){
e[++c]=(E){v,w,fir[u]};fir[u]=c;
e[++c]=(E){u,0,fir[v]};fir[v]=c;
}int cur[N],d[N];
bool bfs(){
L(i,0,ed) d[i]=0,cur[i]=fir[i];
queue<int>q;q.push(st);d[st]=1;
while(!q.empty()){
int u=q.front(),v;q.pop();
E(i,u) if(!d[v=e[i].v]&&e[i].w)
q.push(v),d[v]=d[u]+1;
}return d[ed];
}int dfs(int u,int fl){
if(u==ed) return fl;int s=0,f,v;
for(int i=cur[u];i;i=e[i].nt){
cur[u]=i;
if(e[i].w&&d[v=e[i].v]==d[u]+1){
f=dfs(v,min(e[i].w,fl));
e[i].w-=f;e[i^1].w+=f;
s+=f;fl-=f;if(!fl) break;
}
}if(!s) d[u]=0;return s;
}int dinic(){int s=0;while(bfs()) s+=dfs(st,INF);return s;}
}fl;
最短路
std::priority_queue
优化的 dijkstra
,时间复杂度 。
code
struct DJ{
vector<pi>g[N];vi dis,vis;
priority_queue<pi>q;
void I(int u,int v,int w){g[u].pb({v,w});}
void init(int x,int s){
dis.resize(s+1);fill(all(dis),INF);
vis.resize(s+1);fill(all(vis),0);
dis[x]=0;q.push({0,x});
}void dij(){
while(!q.empty()){
int u=S(q.top());q.pop();
if(vis[u]) continue;vis[u]=1;
for(auto x:g[u])
if(dis[F(x)]>dis[u]+S(x)){
dis[F(x)]=dis[u]+S(x);
if(!vis[F(x)]) q.push({-dis[F(x)],F(x)});
}
}
}
};
LCA
比较冷门的 dfs 序求 LCA,预处理 ,查询 ,基于 dfs 序和 ST 表。
相比于传统的欧拉序 LCA 更好写,更短,没有双倍的常数和空间。
code
int n,m,u,v,rt;vi g[N];
struct dl{
int dn,dfn[N],d[N],lg[N],st[19][N];
int get(int x,int y){return d[x]<d[y]?x:y;}
void dfs(int u,int f){
st[0][dfn[u]=++dn]=f;d[u]=d[f]+1;
for(int v:g[u]) if(v^f) dfs(v,u);
}int lca(int u,int v){
if(u==v) return u;if((u=dfn[u])>(v=dfn[v])) swap(u,v);
int d=lg[v-u++];return get(st[d][u],st[d][v-(1<<d)+1]);
}void init(){
L(i,1,lg[n]) L(j,1,(n+1-(1<<i)))
st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
};
BIT
就树状数组,加一个权值树状数组的 kth
函数。
code
struct BIT{
int n,c[N];void add(int x,int k){for(;x<=n;x+=lb(x)) c[x]+=k;}
int ask(int x){int s=0;for(;x;x-=lb(x)) s+=c[x];return s;}
int kth(int k){
int r=0,t=0;
for(int i=lg/*log2(值域)*/,x,y;~i;i--){
if((x=r+(1<<i))>n) continue;
if((y=t+c[x])<k) r=x,t=y;
}return a[r];//a 是离散化前的数组
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)