bzoj 4514: [Sdoi2016]数字配对 (二分+费用流)

4514: [Sdoi2016]数字配对

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1191   Solved: 446
[ Submit][ Status][ Discuss]

Description

有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

Input

第一行一个整数 n。
第二行 n 个整数 a1、a2、……、an。
第三行 n 个整数 b1、b2、……、bn。
第四行 n 个整数 c1、c2、……、cn。

Output

 一行一个数,最多进行多少次配对

Sample Input

3
2 4 8
2 200 7
-1 -2 1

Sample Output

4

HINT

 n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5

Source

[ Submit][ Status][ Discuss]

题解:二分+费用流

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#define N 160003
#define M 203
#define LL long long
using namespace std;
const LL inf=1e18;
int a[M],b[M],c[M],n,pd[10000003],tot,ans;
int point[N],next[N],v[N],remain[N];
int point1[N],next1[N],v1[N],remain1[N];
LL val[N],val1[N],cost,dis[N];
int last[N],can[N],s,t,ck,prime[10000000];
void add(int x,int y,int z,LL k)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; val[tot]=k;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; val[tot]=-k;
//	cout<<x<<" "<<y<<" "<<z<<" "<<k<<endl;
}
int addflow(int s,int t)
{
	int now=t; int ans=1000000000;
	while (now!=s) {
		ans=min(ans,remain[last[now]]);
		now=v[last[now]^1];
	}
	now=t;
	while (now!=s) {
		remain[last[now]]-=ans;
		remain[last[now]^1]+=ans;
		now=v[last[now]^1];
	}
	return ans;
}
bool spfa(int s,int t)
{
	for (int i=1;i<=t;i++) dis[i]=-inf,can[i]=0;
	dis[s]=0; can[s]=1;
	queue<int> p; p.push(s);
	//if (ans==6279443)
	// printf("!\n");
	while (!p.empty()) {
		int now=p.front(); p.pop();
		for (int i=point[now];i!=-1;i=next[i])
		 if (dis[v[i]]<dis[now]+val[i]&&remain[i]) {
		 	dis[v[i]]=dis[now]+val[i];
		 	last[v[i]]=i;
		 	if (!can[v[i]]) {
		 		can[v[i]]=1; 
		 		p.push(v[i]);
			 }
		 }
		can[now]=0;
	}
	if (dis[t]==-inf) return false;
	int mx=addflow(s,t);
	ans+=mx;
	//cout<<dis[t]<<" "<<mx<<endl;
	cost+=(LL)mx*(LL)dis[t];
	//if (cost<0) return false;
	return true;
}
bool check(int x)
{
	ans=0; cost=0;
	memcpy(point,point1,sizeof(point));
	memcpy(next,next1,sizeof(next));
	memcpy(v,v1,sizeof(v));
	memcpy(remain,remain1,sizeof(remain));
	memcpy(val,val1,sizeof(val));
	remain[ck]=0; remain[ck^1]=x;
	while (spfa(s,t));
	//cout<<ans<<" "<<cost<<endl;
	if (x==ans&&cost>=0) return true;
	else return false;
}
void calc()
{
	pd[0]=1; pd[1]=1;
	for (int i=2;i<=10000000;i++) {
		if (!pd[i]) {
			prime[++prime[0]]=i;
		}
		for (int j=1;j<=prime[0];j++) {
			if (prime[j]*i>10000000) break;
			pd[i*prime[j]]=1;
			if (i%prime[j]==0) break;
		}
	}
}
bool pri(int x)
{
	for (int i=2;i*i<=x;i++)
	 if (x%i==0) return false;
	return true;
}
int main()
{
	freopen("pair.in","r",stdin);
	freopen("pair.out","w",stdout);
	scanf("%d",&n); int sum=0;
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]),sum+=b[i];
	for (int i=1;i<=n;i++) scanf("%d",&c[i]);
	tot=-1;
	memset(point,-1,sizeof(point));
	calc();
	s=1; t=n*2+2;
	for (int i=1;i<=n;i++) {
		add(s,i+1,b[i],0);
		add(i+n+1,t,b[i],0);
		for (int j=1;j<=n;j++)
		 if (i!=j&&a[i]%a[j]==0) {
		 	int t=a[i]/a[j];
		 	if (t<=10000000) {
		 		if (!pd[t]) add(i+1,j+n+1,b[i],(LL)c[i]*(LL)c[j]),add(j+1,i+n+1,b[j],(LL)c[i]*(LL)c[j]);
		 		//cout<<c[i]<<" "<<c[j]<<" "<<(LL)c[i]*(LL)c[j]<<endl;
		 		continue;
			 } 
			if (pri(t)) add(i+1,j+n+1,b[i],(LL)c[i]*(LL)c[j]),add(j+1,i+n+1,b[j],(LL)c[i]*(LL)c[j]);
		 }
	}
	add(t,t+1,0,0);
	t++;
	ck=tot;
	memcpy(point1,point,sizeof(point));
	memcpy(next1,next,sizeof(next1));
	memcpy(v1,v,sizeof(v1));
	memcpy(remain1,remain,sizeof(remain));
	memcpy(val1,val,sizeof(val));
	int l=0; int r=sum; int ans=0;
	while (l<=r) {
		int mid=(l+r)/2;
	//	cout<<mid<<endl;
		if (check(mid)) ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",ans/2);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值