C++竞赛常用实用代码(5)

前言

所有代码中未加的默认加上“#define ll long long”

Dinic的板子打错了好多遍,现在改过来了,非常抱歉

上一页:C++竞赛常用实用代码(4)

目录

线性筛求欧拉函数

线性筛求莫比乌斯函数

快速傅里叶变换(FFT)

快速数论变换(NTT)

快速沃尔什变换FWT(改进)

快速莫比乌斯变换(FMT)

匈牙利算法模板

最大流Dinic算法模板

二分图最大权完美匹配KM算法模板

SPFA求费用流模板


线性筛求欧拉函数

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_IFFT(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;//返回数组长度
}
inline int FFT(cp*a,int n){return FFT_IFFT(a,n,1);}
inline int IFFT(cp*a,int n){return FFT_IFFT(a,n,-1);}

快速数论变换(NTT)

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;
}
#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_INTT(int*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]]);
	}
	for(int mid=1;mid<n;mid<<=1){
		ll tmp=ksm(g,(mod-1)/(mid<<1),mod),om=1,y;
		if(inv<0)tmp=ksm(tmp,mod-2,mod);
		for(int i=0;i<n;i+=(mid<<1),om=1)
			for(int j=0;j<mid;j++,om=(om*tmp)%mod)
				y=om*a[i+j+mid]%mod,a[i+j]=(y+a[i+j])%mod,a[i+j+mid]=((mod-y<<1)+a[i+j])%mod;
	}
    if(inv<0)for(int i=0,in=ksm(n,mod-2,mod);i<n;i++)a[i]=1ll*in*a[i]%mod;//逆变换乘n的逆元
	return n;//返回数组大小
}
inline int NTT(int*a,int n){return NTT_INTT(a,n,1);}
inline int INTT(int*a,int n){return NTT_INTT(a,n,-1);}

快速沃尔什变换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;
}

二分图最大权完美匹配KM算法模板

递归版(O(n^4)):

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版(O(n^3)):

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];
}

Always to be continued(持续更新中)...

猜你喜欢

转载自blog.csdn.net/weixin_43960287/article/details/112391449