有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。
第一行一个整数 n。
第二行 n 个整数 a1、a2、……、an。
第三行 n 个整数 b1、b2、……、bn。
第四行 n 个整数 c1、c2、……、cn。
一行一个数,最多进行多少次配对
3 2 4 8 2 200 7 -1 -2 1
4
n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5
啊啊啊啊啊费用流连边的时候把流量和费用搞混了调了两个小时QWQ
考场上主要遇到了两个问题:
1.如何保证费用大于0的时候流量最大
2.如何保证每个点不被重复使用
对于第一个问题,我们可以贪心解决
即在增广的过程中,如果发现当前路径继续增光不满足条件,那么增广到上限后的最大流量就是答案
对于第二个问题,我们可以把每个数质因数分解后,按照指数的奇偶分为左边和右边,这样连边的话就不会有重复了
#include<bits/stdc++.h>
#define int long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int MAXN=1e5+10;
const int INF=1e16+10;
char buf[1<<23],*p1=buf,*p2=buf;
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int N,S=0,T=23333;
int A[MAXN],B[MAXN],C[MAXN],cnt[MAXN];
struct node
{
int u,v,w,f,nxt;
}edge[MAXN];
int head[MAXN],num=0;
inline void add_edge(int x,int y,int z,int f)
{
edge[num].u=x;
edge[num].v=y;
edge[num].w=z;
edge[num].f=f;
edge[num].nxt=head[x];
head[x]=num++;
}
void AddEdge(int x,int y,int z,int f)
{
add_edge(x,y,z,f);
add_edge(y,x,-z,0);
}
inline int PrimeCut(int x)
{
int tot=0;
for(int i=2;i<=sqrt(x);i++)
while(x%i==0) x/=i,tot++;
return x>1?tot+1:tot;
}
namespace Liu
{
int dis[MAXN],vis[MAXN],Pre[MAXN],ansflow=0,anscost=0;
bool SPFA()
{
queue<int>q;
q.push(S);
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(Pre,0,sizeof(Pre));
dis[S]=0;
while(q.size()!=0)
{
int p=q.front();q.pop();
vis[p]=0;
for(int i=head[p];i!=-1;i=edge[i].nxt)
{
if(edge[i].f>0&&dis[edge[i].v]>dis[p]+edge[i].w)
{
dis[edge[i].v]=dis[p]+edge[i].w;
Pre[edge[i].v]=i;
if(!vis[edge[i].v])
q.push(edge[i].v),
vis[edge[i].v]=1;
}
}
}
return dis[T]<INF;
}
bool f()
{
int nowflow=INF;
for(int i=T;i!=S;i=edge[Pre[i]].u)
nowflow=min(nowflow,edge[Pre[i]].f);
if(anscost+nowflow*(-dis[T]) < 0)
{
ansflow+=anscost/dis[T];
return 0;
}
for(int i=T;i!=S;i=edge[Pre[i]].u)
edge[Pre[i]].f-=nowflow,
edge[Pre[i]^1].f+=nowflow;
anscost+=nowflow*(-dis[T]);
ansflow+=nowflow;
// printf("%d\n",ansflow);
return 1;
}
void MCMF()
{
bool flag=0;
while(SPFA())
{
if( !f() )
{
flag=1;
printf("%d",ansflow);
break;
}
}
if(flag==0) printf("%d",ansflow);
}
}
void Work()
{
for(int i=1;i<=N;i++) cnt[i]=PrimeCut(A[i]);
for(int i=1;i<=N;i++)
cnt[i]&1?AddEdge(S,i,0,B[i]):
AddEdge(i+N,T,0,B[i]);
for(int i=1;i<=N;i++)
{
if(cnt[i]&1)
{
for(int j=1;j<=N;j++)
if( (cnt[i]+1==cnt[j]&&A[j]%A[i]==0) || (cnt[j]+1==cnt[i]&&A[i]%A[j]==0) )
AddEdge(i,j+N,-C[i]*C[j],INF);
}
}
Liu::MCMF();
}
main()
{
#ifdef WIN32
freopen("a.in","r",stdin);
#else
#endif
memset(head,-1,sizeof(head));
N=read();
for(int i=1;i<=N;i++) A[i]=read();
for(int i=1;i<=N;i++) B[i]=read();
for(int i=1;i<=N;i++) C[i]=read();
Work();
return 0;
}