题目大意:
有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。
(题目很清晰啊,就不转述了)
题目分析:
如果一个数ai能整除aj并且商是一个质数,那么对ai和aj分解质因数,ai和aj的质因子的个数一定差1,一定是一奇一偶的。也就是说质因子个数同奇同偶的两个数不能配对。
所以就是说这些点可以构成一个二分图,一侧是质因子个数为奇的,另一侧是质因子个数为偶的。
这样我们可以从源点向左侧的点连一条容量为bi,费用为0的边;从右侧的点到汇点连一条容量为bi费用为0的边,代表第i个点只能选择bi次。
然后在暴力枚举左侧的点和右侧的点,把可以配对的两个点连一条容量为INF,费用为ci*cj的边。代表配对次数不限,但是会获得 ci * cj的价值。
题目要求总价值和不小于0,就跑最大费用最大流。
因为我们每次做spfa都是在有可行流的前提下找到价值最高的流,那么每次获得的价值就是单调不减的,那只要跑到总价值小于0之前就跳出即可。
注意事项:
1、判质数一定不要用你筛质数时候用的那个数组去判断,因为两数相除得出的数会爆出你的质数表(一定要让我把这段痛苦的经历说出来啊啊啊,我居然在判断质数这个地方卡了三天,我把我的筛素数,建图,spfa,费用流的所有部分都检查了若干遍,结果居然是判素数的时候出错了QAQ,一定要好好判素数);
2、容量和价值用long long类型来存,价值相乘会爆int的;
3、小技巧:INF可以赋值成 -0x8f8f8f8f8f8f8f8f ,这样你就在spfa里就可以memset(f,0x8f,sizeof(f)) 来赋最小值了,这样做的最小值就是-INF(虽然没什么太大用,但是代码会显得很好看……吧大概)。
代码如下:
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<queue>
#define N 300000
using namespace std;
const long long INF=-0x8f8f8f8f8f8f8f8fll;
int n,S,T;
long long ans;
int pri[31634],tot;
bool e[31634],pc[N];
int a[N],d[N];
long long b[N],c[N];
int fir[N],nes[N],v[N],top=1,bak[N],road[N];
long long q[N],w[N],f[N];
bool prime(int x)
{
for(int i=1;i<=tot;i++)
if(x==pri[i]) return true;
else if(x%pri[i]==0) return false;
return true;
}
void edge(int x,int y,long long z,long long p)
{
top++;
bak[top]=x; v[top]=y;
q[top]=z; w[top]=p;
nes[top]=fir[x];fir[x]=top;
return;
}
#define edge(x,y,z,p) edge(x,y,z,p),edge(y,x,0,-p)
bool spfa()
{
queue<int> V;
memset(f,0x8f,sizeof(f));
V.push(S); f[S]=0; pc[S]=true;
while(!V.empty())
{
int c=V.front();
for(int t=fir[c];t;t=nes[t])
{
if(!q[t] || f[v[t]]>=f[c]+w[t]) continue;
f[v[t]]=f[c]+w[t]; road[v[t]]=t;
if(pc[v[t]]) continue;
V.push(v[t]); pc[v[t]]=true;
}
V.pop(); pc[c]=false;
}
return f[T]!=-INF;
}
void fly()
{
long long cs=0;
while(spfa())
{
long long c=INF;
for(int t=T;t!=S;t=bak[road[t]]) c=min(c,q[road[t]]);
if(f[T]*c+cs>=0) {ans+=c; cs+=f[T]*c;}
else {ans+=cs/(-f[T]); break;}
for(int t=T;t!=S;t=bak[road[t]])
{
q[road[t]]-=c;
q[road[t]^1]+=c;
}
}
return;
}
int main()
{
scanf("%d",&n);
S=n+1; T=S+1;
for(int i=2;i<=31633;i++) if(!e[i])
for(int j=i+i;j<=31633;j+=i) e[j]=true;
for(int i=2;i<=31633;i++) if(!e[i]) pri[++tot]=i;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
int t=a[i],sum=0,radical=sqrt(a[i]);
for(int j=1;j<=tot;j++)
{
while(t%pri[j]==0) sum++,t/=pri[j];
if(t==1) break;
if(pri[j]>radical) break;
}
if(t!=1) sum++;
d[i]=sum%2;
}
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<=n;i++)
{
if(d[i]) edge(S,i,b[i],0);
else {edge(i,T,b[i],0); continue;}
for(int j=1;j<=n;j++)
{
if(d[j]) continue;
int x=a[i],y=a[j];
if(x<y) swap(x,y);
if(x%y!=0) continue;
if(prime(x/y)) edge(i,j,INF,c[i]*c[j]);
}
}
fly();
printf("%lld",ans);
return 0;
}