「SDOI2016」数字配对

「SDOI2016」数字配对

题目大意

传送门

题解

\(a_i\)\(a_j\) 的倍数,且 \(\frac{a_i}{a_j}\) 是一个质数,则将 \(a_i,a_j\) 质因数分解后,其质因子的次数和相差为 \(1\)

由此我们可以想到根据质因子次数和的奇偶性对 \(a_i\) 进行分组,不难发现会被分成两组。这让我们联想到了二分图。

我们考虑采用费用流求解。

首先我们可以将源点 \(s\) 向其中一组点连容量为 \(b_i\),费用为 \(0\) 的边,然后从另外一组点的每个点向汇点 \(t\) 连容量为 \(b_i\),费用为 \(0\) 的边,限制每个数能够被配对的次数。

然后我们可以在满足条件的点对 \((a_i,a_j)\) 间连一条容量为 \(\min(b_i,b_j)\),费用为 \(c_i\cdot c_j\) 的边。

最后跑最大费用最大流即可。

这里题目要求费用非负,所以在统计答案的时候处理一下使其在非负的条件下流最大即可。

注意有的地方需要开 \(\text{long long}\)

/*---Author:HenryHuang---*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e2+5;
typedef long long ll;
struct edge{
	int to,nex;
	ll w,v;
}e[maxn*maxn*4];
int head[maxn],cur[maxn],tot=1;
void add(int a,int b,ll c,ll d){
	e[++tot]=(edge){b,head[a],c,d};
	head[a]=cur[a]=tot;
}
void add_edge(int a,int b,ll c,ll d){
	add(a,b,c,d);
	add(b,a,0,-d);
}
int solve(int n){
	int i=2,ans=0;
	while(i*i<=n){
		while(n%i==0) ++ans,n/=i;
		++i;
	}
	if(n!=1) ++ans;
	return ans; 
}
int a[maxn],b[maxn],c[maxn];
int cnt[maxn];
int n,m,s,t;
ll dis[maxn];
bool vis[maxn];
bool spfa(){
	for(int i=0;i<=n+1;++i) cur[i]=head[i],dis[i]=-1ll<<60;
	memset(vis,0,sizeof vis);
	queue<int> Q;
	dis[s]=0,vis[s]=1,Q.push(s);
	while(!Q.empty()){
		int u=Q.front();Q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].nex){
			int v=e[i].to;
			if(e[i].w&&dis[v]<dis[u]+e[i].v){
				dis[v]=dis[u]+e[i].v;
				if(!vis[v]) vis[v]=1,Q.push(v);
			}
		}
	}
	return dis[t]>-1ll<<60;
}
int dfs(int u,ll in){
	if(u==t) return in;
	ll out=0,tmp;
	vis[u]=1;
	for(int i=cur[u];i;i=e[i].nex){
		int v=e[i].to;cur[u]=i;
		if((!vis[v])&&e[i].w&&dis[v]==dis[u]+e[i].v&&(tmp=dfs(v,min(in,e[i].w)))){
			e[i].w-=tmp,e[i^1].w+=tmp;
			in-=tmp,out+=tmp;
		}
	}
	if(!out) dis[u]=0;
	vis[u]=0;
	return out;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=n;++i) cin>>b[i];
	for(int i=1;i<=n;++i) cin>>c[i];
	s=0,t=n+1;
	for(int i=1;i<=n;++i){
		cnt[i]=solve(a[i]);
	}
	for(int i=1;i<=n;++i){
		if(cnt[i]&1) add_edge(s,i,b[i],0);
		else add_edge(i,t,b[i],0);
		for(int j=1;j<=n;++j){
			if(cnt[i]&1)
				if((a[i]%a[j]==0&&cnt[j]+1==cnt[i])||(a[j]%a[i]==0&&cnt[i]+1==cnt[j]))
					add_edge(i,j,1<<30,1ll*c[i]*c[j]);
		}
	}
	ll ans=0,now=0,d=0;
	int flag=0;
	while(spfa()){
		memset(vis,0,sizeof vis);
		while(d=dfs(s,1ll<<60),d!=0){
			memset(vis,0,sizeof vis);
			if(now+d*dis[t]<0){
				ans+=now/(-dis[t]);
				flag=1;break;
			}
			ans+=d;
			now+=d*dis[t];
		}
		if(flag) break;
	}
	cout<<ans<<'\n';
	return 0;
}
posted @ 2020-06-05 16:12  Henry__Huang  阅读(173)  评论(0编辑  收藏  举报