bzoj4514【SDOI2016】数字配对

4514: [Sdoi2016]数字配对

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 730   Solved: 311
[ 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

鸣谢Menci上传




费用流的应用

对于所有数字,可以分为两类,分解质因数后指数和为奇数和偶数。配对关系也只能在两类之间,于是就构成一个二分图的模型。建图之后跑费用流就可以了。

这里还有一个限制,费用总和不小于0,也就是每一步要让费用尽可能大。所以我们将所有费用取反,然后每次SPFA后判断是否会使费用大于0,这样就可以保证了费用的限制。

注意程序第81行的ans-=(cost/dis[t])。




#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 210
#define maxm 30000
#define inf 1000000000000000000ll
using namespace std;
int n,s,t,cnt=1,tot,totx,toty,ans;
int a[maxn],b[maxn],head[maxn],p[maxn],pri[32005],fx[maxn],fy[maxn];
ll c[maxn],dis[maxn];
bool inq[maxn],vst[32005];
struct edge_type{int next,from,to,v;ll c;}e[maxm];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline bool judge(int x,int y)
{
	if (!x||!y) return false;
	if (x<y) swap(x,y);
	if (x%y!=0) return false;
	x/=y;
	F(i,1,tot)
	{
		if (pri[i]>=x) break;
		if (x%pri[i]==0) return false;
	}
	return true;
}
inline void add_edge(int x,int y,int v,ll c)
{
	e[++cnt]=(edge_type){head[x],x,y,v,-c};head[x]=cnt;
	e[++cnt]=(edge_type){head[y],y,x,0,c};head[y]=cnt;
}
inline bool spfa()
{
	queue<int> q;
	memset(inq,false,sizeof(inq));
	F(i,1,t) dis[i]=inf;
	dis[s]=0;inq[s]=true;q.push(s);
	while (!q.empty())
	{
		int x=q.front();q.pop();inq[x]=false;
		for(int i=head[x];i;i=e[i].next)
		{
			int y=e[i].to;
			if (e[i].v&&dis[y]>dis[x]+e[i].c)
			{
				dis[y]=dis[x]+e[i].c;
				p[y]=i;
				if (!inq[y]) q.push(y),inq[y]=true;
			}
		}
	}
	return dis[t]!=inf;
}
inline void mcf()
{
	ll cost=0;
	while (spfa())
	{
		int tmp=1000000000;
		for(int i=p[t];i;i=p[e[i].from]) tmp=min(tmp,e[i].v);
		if (cost+dis[t]*tmp<=0)
		{
			cost+=dis[t]*tmp;ans+=tmp;
			for(int i=p[t];i;i=p[e[i].from]) e[i].v-=tmp,e[i^1].v+=tmp;
		}
		else
		{
			ans-=(cost/dis[t]);
			return;
		}
	}
}
int main()
{
	n=read();
	s=n+1;t=n+2;
	F(i,1,n) a[i]=read();
	F(i,1,n) b[i]=read();
	F(i,1,n) c[i]=read();
	F(i,2,32000)
	{
		if (!vst[i]) pri[++tot]=i;
		F(j,1,tot)
		{
			if (i*pri[j]>32000) break;
			vst[i*pri[j]]=true;
			if (i%pri[j]==0) break;
		}
	}
	F(i,1,n)
	{
		int tmp=a[i],num=0;
		F(j,1,tot)
		{
			while (tmp%pri[j]==0) tmp/=pri[j],num++;
			if (tmp==1) break;
		}
		if (num&1) fx[++totx]=i;
		else fy[++toty]=i;
	}
	F(i,1,totx) F(j,1,toty) if (judge(a[fx[i]],a[fy[j]])) add_edge(fx[i],fy[j],1000000000,c[fx[i]]*c[fy[j]]);
	F(i,1,totx) add_edge(s,fx[i],b[fx[i]],0);
	F(i,1,toty) add_edge(fy[i],t,b[fy[i]],0);
	mcf();
	printf("%d\n",ans);
}


你可能感兴趣的:(费用流,bzoj)