前言
所有代码中未加的默认加上“#define ll long long”
Dinic的板子打错了好多遍,现在改过来了,非常抱歉
上一页:C++竞赛常用实用代码(4)
目录
下一页:C++竞赛常用实用代码(6)
线性筛求欧拉函数
int ph[MAXN];
bool nop[MAXN];
vector<int>pri;//素数集
inline void getphi(int n){//套用欧拉筛
nop[0]=nop[1]=1;
for(int i=1;i<=n;i++)ph[i]=i;
for(int a=2;a<=n;a++){
if(!nop[a])pri.push_back(a),ph[a]=a-1;
for(int i=0,u;i<pri.size()&&pri[i]*a<=n;i++){
u=pri[i]*a,nop[u]=1;
if(a%pri[i]==0)ph[u]=ph[a]*pri[i],i=MAXN;
else ph[u]=ph[a]*ph[pri[i]];
}
}
}
线性筛求莫比乌斯函数
int mu[MAXN];
bool nop[MAXN];
vector<int>pri;//素数集
inline void getmu(int n){//套用欧拉筛
mu[1]=1,nop[0]=nop[1]=1;
for(int a=2;a<=n;a++){
if(!nop[a])pri.push_back(a),mu[a]=-1;
for(int i=0,u;i<pri.size()&&pri[i]*a<=n;i++){
u=pri[i]*a,nop[u]=1;
if(a%pri[i]==0)mu[u]=0,i=MAXN;
else mu[u]=-mu[a];
}
}
}
快速傅里叶变换(FFT)
#define cp complex<double> //记得加上<complex>头文件
const double pi=acos(-1);
int rev[MAXN];
inline int FFT(cp*a,int N,int inv){
int bit=0,n=N;
while((1<<bit)<n)bit++;n=(1<<bit);//把n扩成2的次幂
for(int i=0;i<n;i++){
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
if(i<rev[i])swap(a[i],a[rev[i]]);
}cp tmp,omega,y;
for(int mid=1;mid<n;mid<<=1){
tmp=cp(cos(pi/mid),inv*sin(pi/mid)),omega=cp(1,0);
for(int i=0;i<n;i+=(mid<<1),omega=cp(1,0))
for(int j=0;j<mid;j++,omega*=tmp)
y=omega*a[i+j+mid],a[i+j]+=y,a[i+j+mid]=a[i+j]-y-y;
}
if(inv<0)for(int i=0;i<n;i++)a[i].real()/=n;//逆变换要除以n
return n;//返回数组长度
}
快速数论变换(NTT)
#define mod 998244353ll //模数一般为998244353
#define g 3ll //原根
int rev[MAXN<<1];
inline ll ksm(ll a,ll b,ll mo){//快速幂
ll res=1;
for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
return res;
}
inline int NTT(ll*a,int N,int op){
int bit=0,n=N;while((1<<bit)<n)bit++;n=(1<<bit);
for(int i=0;i<n;i++){
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
if(i<rev[i])swap(a[i],a[rev[i]]);
}
for(int m=1,mi=(MOD-1)/(m<<1);m<n;m<<=1,mi>>=1){
ll tmp=op>0?ksm(g,mi,MOD):ksm(g,MOD-mi-1,MOD),om=1,y;
for(int i=0;i<n;i+=(m<<1),om=1)
for(int j=i;j<i+m;j++,om=om*tmp%MOD)
y=om*a[j+m]%MOD,a[j]=(a[j]+y)%MOD,a[j+m]=(a[j]+((MOD-y)<<1))%MOD;
}
if(op<0){ll inv=ksm(n,MOD-2,MOD);
for(int i=0;i<n;i++)a[i]=a[i]*inv%MOD;
}return n;
}
#undef g
快速沃尔什变换FWT(改进)
//所有函数中传入inv=1表示正变换,-1表示逆变换
inline void FWTOR(int*a,int n,int inv){ //按位或
for(int k=2;k<=n;k<<=1)//k的枚举从大到小和从小到大,结果是完全一样的
for(int i=0;i<n;i+=k)
for(int j=i;j<i+(k>>1);j++)
a[j+(k>>1)]=(a[j+(k>>1)]+a[j]*inv+MOD)%MOD;
}
inline void FWTAND(int*a,int n,int inv){ //按位与
for(int k=2;k<=n;k<<=1)
for(int i=0;i<n;i+=k)
for(int j=i;j<i+(k>>1);j++)
a[j]=(a[j]+a[j+(k>>1)]*inv+MOD)%MOD;
}
//此处应有个快速幂
inline void FWTXOR(int*a,int n,int inv){ //异或
ll in2=inv>0?1:ksm(2,MOD-2,MOD),a0,a1;
for(int k=2;k<=n;k<<=1)
for(int i=0;i<n;i+=k)
for(int j=i;j<i+(k>>1);j++)
a0=a[j],a1=a[j+(k>>1)],a[j]=(a0+a1)%MOD*in2%MOD,a[j+(k>>1)]=(a0-a1+MOD)%MOD*in2%MOD;
}
快速莫比乌斯变换(FMT)
主要用来代替FWT的按位或、与部分,用法相同
inline void FMTOR(int*a,int n,int inv){//按位或
for(int k=1;k<n;k<<=1)
for(int i=0;i<n;i++)
if(~i&k)a[i|k]=(a[i|k]+a[i]*inv+MOD)%MOD;
}
inline void FMTAND(int*a,int n,int inv){//按位与
for(int k=1;k<n;k<<=1)
for(int i=n-1;i>=0;i--)
if(i&k)a[i^k]=(a[i^k]+a[i]*inv+MOD)%MOD;
}
更多有关快速沃尔什变换和莫比乌斯变换
匈牙利算法模板
int mat[MAXN];
vector<int>G[MAXN];
bool vis[MAXN];
inline bool findh(int x){
for(int i=0;i<G[x].size();i++)
if(!vis[G[x][i]]){
int v=G[x][i];vis[v]=1;
if(mat[v]<0||findh(mat[v])){
mat[v]=x;return 1;
}
}
return 0;
}
inline int hungary(){
memset(mat,-1,sizeof(mat));
int res=0;
for(int i=0;i<n;i++){
memset(vis,0,sizeof(vis));
if(findh(i))res++;
}
return res;
}
最大流Dinic算法模板
#define INF 0x3f3f3f3f
int d[MAXN],cur[MAXN];
ll f[MAXN];
struct edge{
int v,id,fd;edge(){}
edge(int V,int I,int F){v=V,id=I,fd=F;}
};
vector<edge>G[MAXN];
queue<int>q;
inline bool bfs(int S,int T){
while(!q.empty())q.pop();
memset(d,-1,sizeof(d));
d[S]=0,q.push(S);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<G[u].size();i++)
if(f[G[u][i].id]>0&&d[G[u][i].v]<0)
d[G[u][i].v]=d[u]+1,q.push(G[u][i].v);
}
return d[T]>=0;
}
inline ll dfs(int x,ll lim,int T){
if(x==T)return lim;
ll res=lim;
for(int i=cur[x];i<G[x].size()&&res>0;i++){
cur[x]=i;
int v=G[x][i].v,a=G[x][i].id,b=G[x][i].fd;
if(f[a]>0&&d[v]==d[x]+1){
int ad=dfs(v,min(res,f[a]),T);
f[a]-=ad,f[b]+=ad,res-=ad;
}
}
return lim-res;
}
inline ll dinic(int S,int T){
ll res=0;
while(bfs(S,T)){
memset(cur,0,sizeof(cur));
while(ll ad=dfs(S,INF,T))res+=ad;
}
return res;
}
前向星改进版:
#define ll long long
#define INF 1e17
struct edge{
int v,to;ll f;edge(){}
edge(int V,ll F,int T){v=V,f=F,to=T;}
}e[1000005];
int EN=1,G[MAXN],ps;
inline void addedge(int u,int v,ll f){
e[++EN]=edge(v,f,G[u]),G[u]=EN;
e[++EN]=edge(u,0,G[v]),G[v]=EN;
}
int cur[MAXN],ds[MAXN];
queue<int>q;
inline bool dcbfs(int S,int T){
for(int i=1;i<=ps;i++)ds[i]=-1;
while(!q.empty())q.pop();
q.push(S),ds[S]=0;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=G[u];i;i=e[i].to){
int v=e[i].v;
if(e[i].f>0&&ds[v]<0)ds[v]=ds[u]+1,q.push(v);
}
}return ds[T]>=0;
}
inline ll dcdfs(int x,int T,ll lim){
if(x==T)return lim;
ll res=lim;
for(int i=cur[x];i&&res;i=e[i].to){
cur[x]=i;
int v=e[i].v;
if(ds[v]==ds[x]+1&&e[i].f>0){
ll ad=dcdfs(v,T,min(e[i].f,res));
e[i].f-=ad,res-=ad,e[i^1].f+=ad;
}
}return lim-res;
}
inline ll dinic(int S,int T){
ll res=0;
while(dcbfs(S,T)){
for(int i=1;i<=ps;i++)cur[i]=G[i];
while(ll ad=dcdfs(S,T,INF))res+=ad;
}
return res;
}
二分图最大权完美匹配KM算法模板
递归版():
int n,mat[505];
ll w[505][505],exg[505],exb[505],lak[505];
bool vg[505],vb[505];
inline bool dfs(int x){
vg[x]=1;
for(int v=1;v<=n;v++)
if(!vb[v]){
ll gap=exg[x]+exg[v]-w[x][v];
if(gap==0){
vb[v]=1;
if(mat[v]<0||dfs(mat[v])){
mat[v]=x;return 1;
}
}
else lak[v]=min(lak[v],gap);
}
return 0;
}
inline ll KM(){
memset(mat,-1,sizeof(mat));
mst0(exb);
for(int i=1;i<=n;i++){
exg[i]=w[i][1];
for(int j=2;j<=n;j++)
exg[i]=max(exg[i],w[i][j]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)lak[j]=INF;
while(1){
mst0(vg);mst0(vb);
if(dfs(i))break;
ll ad=INF;
for(int j=1;j<=n;j++)
if(!vb[j])ad=min(ad,lak[j]);
for(int j=1;j<=n;j++){
if(vg[j])exg[j]-=ad;
if(vb[j])exb[j]+=ad;
else lak[j]-=ad;
}
}
}
ll res=0;
for(int i=1;i<=n;i++)res+=w[mat[i]][i]-ADD;
return res;
}
BFS版():
#define mst0(x) memset(x,0,sizeof(x))
const ll ADD=19980731;//为了方便初始化,边权都加上某个值
int n,mat[505],pre[505];
ll w[505][505],exg[505],exb[505],lak[505];
bool vb[505];
inline void bfs(int pg){
int x,y=0,by=0;ll ad;
mst0(pre);
memset(lak,0x7f,sizeof(lak));
mat[y]=pg;
do{
x=mat[y],ad=INF,vb[y]=1;
for(int i=1;i<=n;i++)
if(!vb[i]){
ll gap=exg[x]+exb[i]-w[x][i];
if(lak[i]>gap)lak[i]=gap,pre[i]=y;
if(lak[i]<ad)ad=lak[i],by=i;
}
for(int i=0;i<=n;i++){
if(vb[i])exg[mat[i]]-=ad,exb[i]+=ad;
else lak[i]-=ad;
}
y=by;
}while(mat[y]!=0);
while(y)mat[y]=mat[pre[y]],y=pre[y];
}
inline ll KM(){
mst0(mat);mst0(exb);mst0(exg);
for(int i=1;i<=n;i++)mst0(vb),bfs(i);
ll res=0;
for(int i=1;i<=n;i++)res+=w[mat[i]][i]-ADD;
return res;
}
SPFA求费用流模板
重拾SPFA
ll w[MAXM],c[MAXM],f[MAXN],d[MAXN],ansf,ansc;
int pre[MAXN],las[MAXN];
struct edge{
int v,id;edge(){}
edge(int V,int I){v=V,id=I;}
};
vector<edge>G[MAXN];
bool vis[MAXN];
queue<int>q;
inline bool SPFA(int S,int T){
memset(d,0x7f,sizeof(d));
memset(vis,0,sizeof(vis));
memset(f,0x7f,sizeof(f));
q.push(S),d[S]=0,vis[S]=1,pre[T]=-1;
while(!q.empty()){
int u=q.front();q.pop(),vis[u]=0;
for(int i=0;i<G[u].size();i++){
int v=G[u][i].v,a=G[u][i].id;
if(w[a]>0&&d[v]>d[u]+c[a]){
d[v]=d[u]+c[a],f[v]=min(f[u],w[a]);
pre[v]=u,las[v]=a;
if(!vis[v])vis[v]=1,q.push(v);
}
}
}
return pre[T]>=0;
}
inline void ildfs(int S,int T){ //Illusory-DFS,虚假深搜
ansf+=f[T],ansc+=f[T]*d[T];
for(int x=T;x!=S;x=pre[x])
w[las[x]]-=f[T],w[las[x]^1]+=f[T];
}
前向星改进版:
#define ll long long
#define INF 1e17
struct edge{
int v,to;ll c,f;edge(){}
edge(int V,ll C,ll F,int T){v=V,c=C,f=F,to=T;}
}e[1000005];
int EN=1,G[MAXN],ps;
inline void addedge(int u,int v,ll c,ll f){
e[++EN]=edge(v,c,f,G[u]),G[u]=EN;
e[++EN]=edge(u,-c,0,G[v]),G[v]=EN;
}
ll ds[MAXN],f[MAXN],ansf,ansc;
int pre[MAXN];
bool inq[MAXN];
queue<int>q;
inline bool SPFA(int S,int T){
for(int i=1;i<=ps;i++)ds[i]=f[i]=INF;
q.push(S),inq[S]=1,ds[S]=0,pre[S]=0,pre[T]=-1;
while(!q.empty()){
int u=q.front();q.pop(),inq[u]=0;
for(int i=G[u];i;i=e[i].to){
int v=e[i].v;
if(e[i].f>0&&ds[v]>ds[u]+e[i].c){
ds[v]=ds[u]+e[i].c,f[v]=min(f[u],e[i].f);
pre[v]=i;
if(!inq[v])inq[v]=1,q.push(v);
}
}
}return pre[T]>=0;
}
inline void ildfs(int S,int T){
ansf+=f[T],ansc+=f[T]*ds[T];
for(int x=T;x^S;x=e[pre[x]^1].v)
e[pre[x]].f-=f[T],e[pre[x]^1].f+=f[T];
}
三种最小生成树算法
算法之前(Kruskal、Boruvka)
#define uns unsigned
int n,fa[MAXN];
inline int findset(int x){ //Kruskal、Boruvka都要用到并查集
return fa[x]==0?x:(fa[x]=findset(fa[x]));
}
struct edge{
int v,w;edge(){}
edge(int V,int W){v=V,w=W;}
};
struct edge_{
int u,v,w;edge_(){}
edge_(int U,int V,int W){u=U,v=V,w=W;}
bool operator<(const edge3&b)const{
if(w!=b.w)return w<b.w;
else if(u!=b.u)return u<b.u;
else return v<b.v;
}
}e[MAXN];
vector<edge>G[MAXN],T[MAXN];//G为原图,T为生成树
vector<edge_>ad;
Kruskal(首选)
适用于边少(≤500000)点多的情况,复杂度
inline void Kruskal(){
ad.clear();
memset(fa,0,sizeof(fa));
for(int u=1;u<=n;u++)
for(uns i=0;i<G[x].size();i++)
ad.push_back(edge_(u,G[x][i].v,G[x][i].w));
sort(ad.begin(),ad.end());
for(uns i=0;i<ad.size();i++){
int u=ad[i].u,v=ad[i].v;
if(findset(u)!=findset(v)){
T[u].push_back(edge(v,ad[i].w));
T[v].push_back(edge(u,ad[i].w));
fa[findset(v)]=findset(u);
}
}
}
Prim(用的少)
Prim适用于点少(≤5000)的完全图,也就是边数为级别的题,复杂度
,它会比Kruskal少一个log,所以说是无奈之选
//前面部分的代码准备不一样,请自行脑补
inline int Prim(){ //邻接矩阵
int an=0; //生成树边的总长
memset(v,0,sizeof(v)),v[1]=1;
for(int i=1;i<=n;i++)w[i]=c[1][i]; //连通块连向其他点的边权
for(int K=1;K<n;K++){
int u=1,ad=0x3f3f3f3f;
for(int i=1;i<=n;i++)if(!v[i]&&w[i]<ad)ad=w[i],u=i;
an+=ad,v[u]=1;
for(int i=1;i<=n;i++)w[i]=min(w[i],c[u][i]);
}
return an;
}
Boruvka(高级)
每次找每个连通块出发边权最小的边并连接,一次下来,连通块数量减半,最多做次。一般情况,它的复杂度是
。
对于点多(≤500000)的完全图,边数为级别,但两点之间的边有特殊规律的题,它可以更快。假设从一个点,找到一条边权最小的连向与自己未连通的点的边,最少需要时间
,那么它的复杂度为
inline void Boruvka(){
int num=n;
memset(fa,0,sizeof(fa));
while(num>1){
ad.clear();
for(int i=1;i<=n;i++)e[i]=edge_(0,0,2e9);
for(int u=1;u<=n;u++)
//下面的找边过程可根据题目的性质优化
for(uns i=0;i<G[u].size();i++){
int v=G[u][i].v,w=G[u][i].w,x=findset(u);
if(findset(u)!=findset(v))
e[x]=min(e[x],edge_(u,v,w));
}
//
for(int i=1;i<=n;i++)if(e[i].u)ad.push_back(e[i]);
for(uns i=0;i<ad.size();i++){
int u=findset(ad[i].u),v=findset(ad[i].v);
if(u!=v){
T[ad[i].u].push_back(edge(ad[i].v,ad[i].w));
T[ad[i].v].push_back(edge(ad[i].u,ad[i].w));
fa[v]=u,num--;
}
}
}
}
浮点数高斯消元
const double MN=1e-13;//判0
double c[MAXN][MAXN],x[MAXN]; //c:增广矩阵,x:变量的解
inline int Gauss(int n,int m,int&tm){//m个变量,n个方程,tm统计行的交换次数(用于计算行列式)
for(int i=0;i<=m;i++)x[i]=0; //预处理
int col,row;
for(row=0,col=0;row<n&&col<m;row++,col++){
int mxr=row;//找当前列绝对值最大的行
for(int i=row+1;i<n;i++)
if(fabs(c[i][col])>fabs(c[mxr][col]))mxr=i;
if(mxr!=row&&(++tm))
for(int j=col;j<=m;j++)swap(c[row][j],c[mxr][j]);
if(fabs(c[row][col])<MN){row--;continue;}//这列已经清零,继续做下一列,但不能放弃这行
for(int i=row+1;i<n;i++){//消元
if(fabs(c[i][col])<MN)continue;
double g=c[i][col]/c[row][col];
c[i][col]=0;
for(int j=col+1;j<=m;j++)c[i][j]-=c[row][j]*g;
}
}
for(int i=row;i<n;i++)//无解
if(fabs(c[i][col])>=MN)return -1;
if(row<m)return m-row;//无数解、有自由元
for(int i=n-1;i>=0;i--){//唯一解
double b=c[i][m];
for(int j=i+1;j<m;j++)b-=c[i][j]*x[j];
x[i]=b/c[i][i];
}
return 0;
}//无解返回-1,唯一解返回0,否则返回自由元个数
*注:这是一个大常数板子,有小常数板子的,不妨推荐给我吧👀
模线性高斯消元
inline int ksm(int a,int b,int mo){//和浮点数唯一不同是求逆元需要用到快速幂
int res=1;
for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
return res;
}
int c[MAXN][MAXN],x[MAXN];
inline int Gauss(int n,int m,int mo,int&tm){//这里默认模数为质数,否则要求phi
for(int i=0;i<=m;i++)x[i]=0;
int col,row;
for(row=0,col=0;row<n&&col<m;row++,col++){
int mxr=row;
for(int i=row+1;i<n;i++)
if(abs(c[i][col])>abs(c[mxr][col]))mxr=i;
if(mxr!=row&&(++tm))
for(int j=col;j<=m;j++)swap(c[row][j],c[mxr][j]);
if(!c[row][col]){row--;continue;}
int inv=ksm(c[row][col],mo-2,mo);//预先存下逆元,保证复杂度仍为n^3
for(int i=row+1;i<n;i++){
if(!c[i][col])continue;
int g=c[i][col]*inv;
c[i][col]=0;
for(int j=col+1;j<=m;j++)
c[i][j]=(c[i][j]-c[row][j]*g%mo+mo)%mo;
}
}
for(int i=row;i<n;i++)//无解
if(c[i][col]>0)return -1;
if(row<m)return m-row;//无数解
for(int i=n-1;i>=0;i--){//唯一解
int b=c[i][m];
for(int j=i+1;j<m;j++)b=(b-c[i][j]*x[j]%mo+mo)%mo;
x[i]=b*ksm(c[i][i],mo-2,mo)%mo;
}
return 0;
}
普通BSGS(大步小步算法)
保证底数与模数互质
ll B;
map<ll,int>mp;
inline ll BSGS(ll a,ll b,ll c){//a^x=b mod c
if(a%c==1&&b%c!=1)return -1;
if(b%c==1)return 0;
if(a%c==b%c)return 1;
B=ceil(sqrt(c))+5,a%=c,b%=c;
ll cg=ksm(a,B,c),g;
mp.clear(),g=b*a%c;
for(int i=1;i<=B;i++)mp[g]=i,g=g*a%c;
g=cg;
for(int i=1;i<=B;i++){
if(mp.find(g)!=mp.end())return B*i-mp[g];
g=g*cg%c;
}
return -1;
}
exBSGS(扩展大步小步算法)
不保证底数与模数互质
ll B;
map<ll,int>mp;
inline ll exBSGS(ll a,ll b,ll c){
if(a%c==1&&b%c!=1)return -1;
if(b%c==1)return 0;
if(a%c==b%c)return 1;
ll v=1,e=0,gc;
while((gc=gcd(a,gcd(b,c)))>1){
b/=gc,c/=gc,v=v*(a/gc)%c,e++;
if(v%c==b%c)return e;
}
if(gcd(a,c)>1)return -1;
B=ceil(sqrt(c))+5,a%=c,b%=c,v%=c;
ll cg=ksm(a,B,c),g;
mp.clear(),g=b*a%c;
for(int i=1;i<=B;i++)mp[g]=i,g=g*a%c;
g=v*cg%c;
for(int i=1;i<=B;i++){
if(mp.find(g)!=mp.end())return B*i-mp[g]+e;
g=g*cg%c;
}
return -1;
}