! SDOI2016数字配对

时间:2020-03-24
本文章向大家介绍! SDOI2016数字配对,主要包括! SDOI2016数字配对使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。


很容易想到最大费用最大流,但我们无法保证费用是非负的

因为每次的最长路都是在逐渐变小的,所以可以贪心

\(dfs\)是先不加费用,完了后根据流量看费用是否非负,如果变负数就贪心选尽可能多的的对数,不然继续

可以看\(mcmf\)函数,一下就明白了

小技巧:\(cn_i\)\(a_i\)质因数分解后,每个质因数的指数和,只可能是相差为1的连边,所以是个二分图,根据每个点\(cn_i\)的奇偶性判断连起点或终点

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=204,inf=1e18;
bitset<40004>bb;
int n,tot,s,t;
int pri[5000],a[N],b[N],c[N],cn[N],vis[N],dis[N];
inline void init(){
	for(int i=2;i<=40000;i++){
		if(!bb[i])pri[++tot]=i;
		for(int j=1;j<=tot&&pri[j]*i<=40000;j++){
			bb[pri[j]*i]=1;
			if(i%pri[j]==0)break;
		}
	}
}
struct edge{
	int v,c,f,nxt;
}e[N*N*2];
int first[N],cur[N],cnt=1;
inline void add(int u,int v,int c,int f){
	e[++cnt]=(edge){v,c,f,first[u]};
	first[u]=cnt;
	e[++cnt]=(edge){u,-c,0,first[v]};
	first[v]=cnt;
}
inline bool spfa(){
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=t;i++)dis[i]=-inf;
	static queue<int>q;
	q.push(t);dis[t]=0;vis[t]=1; 
	while(!q.empty()){
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=first[x],v;i;i=e[i].nxt){
			v=e[i].v;
			if(e[i^1].f&&dis[v]<dis[x]-e[i].c){
				dis[v]=dis[x]-e[i].c;
				if(!vis[v]){q.push(v);vis[v]=1;}
			}
		}
	}
	return dis[s]!=-inf;
}
int dfs(int x,int f){
	if(!f)return 0;
	if(x==t){vis[0]=1;return f;}
	vis[x]=1;
	int used=0;
	for(int &i=cur[x],v,w;i;i=e[i].nxt){
		v=e[i].v;
		if(vis[v]||!e[i].f||dis[v]+e[i].c!=dis[x])continue;
		w=dfs(v,min(f,e[i].f));
		if(!w)continue;
		e[i].f-=w;e[i^1].f+=w;
		f-=w;used+=w;
		if(!f)break;
	}
	return used;
}
inline int mcmf(){
	int flow,ret=0,cost=0;
	while(spfa()){
		memcpy(cur,first,sizeof(first));
		vis[0]=1;
		while(vis[0]){
			memset(vis,0,sizeof(vis));
			flow=dfs(s,inf);
			if(cost+flow*dis[s]>=0){
				cost+=flow*dis[s];
				ret+=flow;
			}
			else return ret+cost/(-dis[s]);
		}
	}
	return ret;
}
signed main(){
	init();
	n=read();s=n+1;t=s+1; 
	for(int i=1,x;i<=n;i++){
		a[i]=x=read();
		for(int j=1;j<=tot&&pri[j]*pri[j]<=a[i];j++)
			while(x%pri[j]==0){
				x/=pri[j];
				cn[i]++;
			}
		if(x>1)cn[i]++;
	} 
	for(int i=1;i<=n;i++){
		b[i]=read();
		if(cn[i]&1)add(s,i,0,b[i]);
		else add(i,t,0,b[i]);
	}
	for(int i=1;i<=n;i++)c[i]=read();
	for(int i=1;i<=n;i++){
		if(!(cn[i]&1))continue;
		for(int j=1;j<=n;j++){
			if(cn[j]&1)continue;
			if((a[i]%a[j]==0&&cn[i]==cn[j]+1)||(a[j]%a[i]==0&&cn[i]==cn[j]-1))
				add(i,j,c[i]*c[j],min(b[i],b[j]));
		}
	}
	cout<<mcmf();
	return (0-0);
}

原文地址:https://www.cnblogs.com/aurora2004/p/12557796.html