BZOJ4514: [Sdoi2016]数字配对

BZOJ4514: [Sdoi2016]数字配对

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1101   Solved: 419

题解:
把所有的数字分解质因数,两个数字相除如果想等于一个质数,那么他们的分解质因数的个数一定只相差1,于是我们可以用数字分解成质因数个数的奇偶性来建出一个二分图,把奇数的建一条(S,i,b[i],0),把偶数的建一条(i,T,b[i],0)。
枚举二分图两个集合的点, 然后判断哪些奇偶性不同的点之间只差一个质因数,如果只差一个就连一条(i,j,inf,c[i]*c[j]),然后就建完模了。(from,to,flow,value)
本题的目的是让流量最大且费用不小于零,这样我们用Spfa跑最长路,尽可能的去流流量,费用先递增然后递减,直到小于等于零的时候我们结束费用流就可以了,最大流量即我们所能配对配最多的个数, 流过去一个流量 相当于 配对 一个
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define LL long long 
const int N=505;
const int M=100005;
const int inf=1e9;
int n,tot[N],p[N][N],b[N],a[N];
LL c[N],d[N],v[M],w[M];
int to[M],nxt[M],from[M],lj[N],bef[N],cnt=1,S,T;
void ins(int f,int t,LL ww,LL vv)
{
	cnt++,from[cnt]=f,to[cnt]=t,nxt[cnt]=lj[f],lj[f]=cnt,w[cnt]=ww,v[cnt]=vv;
}
void add(int f,int t,LL ww,LL vv){ins(f,t,ww,vv),ins(t,f,0,-vv);}
bool Check(int x,int y)
{
	bool flag=false;
	int len=max(tot[x],tot[y]),lx=1,ly=1;
	while(lx<=len&&ly<=len)
	{
		if(p[x][lx]!=p[y][ly])
		{
			if(flag==true) return false; 
			if(tot[x]<tot[y]) ly++;
			else lx++;
			flag=true;
		}
		else lx++,ly++;
	}
	return true;
}
void Build()
{
	S=0,T=n+1;
	for(int i=1;i<=n;i++)
	{
		if(tot[i]%2==1) add(S,i,b[i],0);
		else add(i,T,b[i],0);
	} 
	for(int i=1;i<=n;i++)
	if(tot[i]%2==1)
	for(int j=1;j<=n;j++) 
	if(tot[j]%2==0&&abs(tot[i]-tot[j])==1)
	if(Check(i,j)) add(i,j,inf,c[i]*c[j]);
}
queue<int>Q;
bool inq[N];
bool Spfa()
{
	for(int i=S;i<=T;i++) d[i]=-1e15;
	d[S]=0;
	Q.push(S);
	while(!Q.empty())
	{
		int x=Q.front();
		Q.pop();
		inq[x]=false;
		for(int i=lj[x];i;i=nxt[i])
		if(w[i]&&d[to[i]]<d[x]+v[i])
		{
			d[to[i]]=d[x]+v[i];
			bef[to[i]]=i;
			if(!inq[to[i]])
			{
				Q.push(to[i]);
				inq[to[i]]=true;
			}
		}
	}
	if(d[T]!=-1e15) return true;
	return false;
}
LL Exflow()
{
	LL ans=0,ret=0;
	while(Spfa())
	{
		LL flow=inf;
		for(int i=T;i!=S;i=from[bef[i]]) flow=min(flow,w[bef[i]]);
		for(int i=T;i!=S;i=from[bef[i]]) w[bef[i]]-=flow,w[bef[i]^1]+=flow;
		if(ans+d[T]*flow<0) {ret+=ans/(-d[T]);break;}
		else ans+=d[T]*flow,ret+=flow;
	}
	return ret;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		int x=a[i];
		for(int j=2;j<=sqrt(x);j++)
		if(x%j==0)
		while(x%j==0) p[i][++tot[i]]=j,x/=j;
		if(x>1) p[i][++tot[i]]=x;
	}
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
	Build();
	printf("%lld",Exflow());
}
 

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]
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值