千万不要想复杂了,倍数和质数仅仅是判断两点之间能不能匹配的条件。。
主要问题还是一个分配问题
一开始想贪心,但好像不对,然后就由反例推出了关系图,然后就是二分匹配问题。。
由于每个点入的量+出的量==数量,由于一次匹配要流两条边,对于原图是很难限制的;
但由于对称性,所以就直接建两个方向的边即可。。
码:
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 999999999999999
#define ll long long
queue<int>q;
int tot=-1,hou[500005],zhong[500005],xia[500005],s,t,qj[500005],n,i,j;
struct la
{
ll x,y,z;
}a[500005];
bool vis[500005];
ll c[500005],d[500005],ans,daan,v[500005];
bool sushu(int o)
{
int i;
for(i=2;i*i<=o;i++)
{
if(o%i==0)return 0;
}
return 1;
}
void jian(int a,int b,ll C,ll d)
{
++tot,hou[tot]=xia[a],xia[a]=tot,c[tot]=d,v[tot]=C,zhong[tot]=b;
}
void jia(int a,int b,ll C,ll d)
{
jian(a,b,C,d);
jian(b,a,0,-d);
}
bool spfa()
{
int i;
// cout<<"P";
for(i=1;i<=t;i++)d[i]=-inf;
qj[s]=-1;
d[s]=0;
q.push(s);
while(!q.empty())
{
int st=q.front();
vis[st]=0;
q.pop();
for(i=xia[st];i!=-1;i=hou[i])
{
int nd=zhong[i];
if(v[i]&&d[nd]<d[st]+c[i])
{qj[nd]=i;
d[nd]=d[st]+c[i];if(vis[nd]==0)q.push(nd);
vis[nd]=1;
}
}
}
if(d[t]==-inf)return 0;
ll o=qj[t],lin=inf;
while(o!=-1)
{
lin=min(lin,v[o]);
o=qj[zhong[o^1]];
}
o=qj[t];
ll lin2=0;
// cout<<lin<<" ";
while(o!=-1)
{
v[o]-=lin;
v[o^1]+=lin;
ans+=lin*c[o];
lin2+=c[o];
o=qj[zhong[o^1]];
} daan+=lin;
if(ans<0)
{
daan-=(-ans)/(-lin2);
if((-ans)%(-lin2)!=0)daan--;
return 0;
}
return 1;
}
ll mcmf()
{
while(spfa());
return daan;
}
int main()
{
memset(xia,-1,sizeof(xia));
scanf("%d",&n);
s=n*2+1;t=n*2+2;
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i].x);
}
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i].y);
}
for(i=1;i<=n;i++)
{
scanf("%lld",&a[i].z);
}
for(i=1;i<n;i++)
for(j=i+1;j<=n;j++)
{
int l1=i,l2=j;
if(a[l1].x<a[l2].x)swap(l1,l2);
int o=a[l1].x/a[l2].x;
if((a[l1].x%a[l2].x==0)&&(sushu(o)))//可以被选
{
jia(l1,l2+n,inf,a[l1].z*a[l2].z);
jia(l2,l1+n,inf,a[l1].z*a[l2].z);
}
}
for(i=1;i<=n;i++)jia(s,i,a[i].y,0),jia(i+n,t,a[i].y,0);
printf("%lld",mcmf()/2);
}