SDOI2016 數字配對

傳送門

裸費用流。
建邊:對於a[i]a[j]=prime&a[j]a[i]\frac{a[i]}{a[j]}=prime \& a[j]|a[i],需要iij+nj+n連,並且jji+ni+n連。費用即爲c[i]c[j]c[i]*c[j],流量無窮大。而源點向左側每個點連邊,費用爲0,流量爲b[i]b[i],右側向匯點也如此。可以發現,這樣相當於是每一種數的個數翻了一倍,所以最後答案除以2即可。
費用流就是每次按照費用找到一條最短路然後增廣。
那麼如果當前增廣後的費用小於了0,那麼就不流滿,增加流量至費用剛好大等於0。否則可以全部流滿。由於總費用是和0作比較,那麼就不存在除以2的問題。

#include<bits/stdc++.h>
#define cs const
#define re register
#define ll long long
cs int N=205,M=162000,oo=1e9;
cs ll OO=1e18;
int n,key[N],num[N],S,T;ll val[N];
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch=gc();T x=0;int f=1;
		while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
		while(isdigit(ch)) x=((x+(x<<2))<<1)+(ch^48),ch=gc();
		return x*f;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}	
using IO::gi;
using IO::gl;
namespace math{
	cs int N=32000;
	int mark[N],P[N],cnt=0;
	inline void linear_sieves(){
		mark[0]=mark[1]=1;
		for(int re i=2;i<N;++i){
			if(!mark[i]) P[++cnt]=i;
			for(int re j=1;j<=cnt&&i*P[j]<N;++j){
				mark[i*P[j]]=1;if(i%P[j]==0) break;
			}
		}
	}
	inline bool isprime(int x){
		if(x<N) return !mark[x];
		for(int re j=1,up=sqrt(x);j<=cnt&&P[j]<=up;++j)
			if(x%P[j]==0) return false;
		return true;
	}
}
using math::isprime;

inline void Min(int &x,int y){if(x>y)x=y;}
namespace FLOW{
	cs int gN=::N<<1|1;
	int Head[gN],Next[M],V[M],W[M],cnt=1;ll C[M];
	int edge[gN],pre[gN],flow[gN],vis[gN];ll dis[gN];
	//w流量,c費用 
	inline void add(int u,int v,int w,ll c){Next[++cnt]=Head[u],V[cnt]=v,W[cnt]=w,C[cnt]=c,Head[u]=cnt;}
	inline bool SPFA(){
		std::queue<int> Q;
		for(int re i=1;i<=T;++i) flow[i]=oo,dis[i]=-OO;
		dis[S]=0,vis[S]=1,Q.push(S);
		while(!Q.empty()){
			int u=Q.front();Q.pop(),vis[u]=0;
			for(int re i=Head[u],v=V[i];i;v=V[i=Next[i]]){
				if(W[i]&&dis[v]<dis[u]+C[i]){
					dis[v]=dis[u]+C[i],edge[v]=i,pre[v]=u;
					Min(flow[v],flow[u]),Min(flow[v],W[i]);
					if(!vis[v]) Q.push(v),vis[v]=1;
				}
			}
		}return dis[T]!=-OO;
	}
	inline int mcf(int ans_flow=0,ll ans_cost=0){
		while(SPFA()){
			if(ans_cost+dis[T]*flow[T]<0){
				ans_flow+=ans_cost/(-dis[T]);break;
			}ans_flow+=flow[T],ans_cost+=dis[T]*flow[T];
			for(int re u=T;u!=S;u=pre[u])
				W[edge[u]]-=flow[T],W[edge[u]^1]+=flow[T];
		}return ans_flow/2;
	}
}
using FLOW::add;
int main(){
//	freopen("2560.in","r",stdin);
	n=gi(),S=n<<1|1,T=S+1,math::linear_sieves();
	for(int re i=1;i<=n;++i) key[i]=gi();
	for(int re i=1;i<=n;++i) num[i]=gi();
	for(int re i=1;i<=n;++i) val[i]=gl();
	for(int re i=1;i<=n;++i)
		for(int re j=1;j<=n;++j)
			if(key[i]%key[j]==0&&isprime(key[i]/key[j]))
				add(i,j+n,oo,val[i]*val[j]),
				add(j+n,i,0,-val[i]*val[j]),
				add(j,i+n,oo,val[i]*val[j]),
				add(i+n,j,0,-val[i]*val[j]);
	for(int re i=1;i<=n;++i) add(S,i,num[i],0),add(i,S,0,0);
	for(int re i=1;i<=n;++i) add(i+n,T,num[i],0),add(T,i+n,0,0);
	printf("%d \n",FLOW::mcf());
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章