网络流算法基于贪心
很容易想到最大费用最大流,但我们无法保证费用是非负的
因为每次的最长路都是在逐渐变小的,所以可以贪心
(dfs)是先不加费用,完了后根据流量看费用是否非负,如果变负数就贪心选尽可能多的的对数,不然继续
可以看(mcmf)函数,一下就明白了
小技巧:(cn_i)是(a_i)质因数分解后,每个质因数的指数和,只可能是相差为1的连边,所以是个二分图,根据每个点(cn_i)的奇偶性判断连起点或终点
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f==1?x:-x;
}
const int N=204,inf=1e18;
bitset<40004>bb;
int n,tot,s,t;
int pri[5000],a[N],b[N],c[N],cn[N],vis[N],dis[N];
inline void init(){
for(int i=2;i<=40000;i++){
if(!bb[i])pri[++tot]=i;
for(int j=1;j<=tot&&pri[j]*i<=40000;j++){
bb[pri[j]*i]=1;
if(i%pri[j]==0)break;
}
}
}
struct edge{
int v,c,f,nxt;
}e[N*N*2];
int first[N],cur[N],cnt=1;
inline void add(int u,int v,int c,int f){
e[++cnt]=(edge){v,c,f,first[u]};
first[u]=cnt;
e[++cnt]=(edge){u,-c,0,first[v]};
first[v]=cnt;
}
inline bool spfa(){
memset(vis,0,sizeof(vis));
for(int i=1;i<=t;i++)dis[i]=-inf;
static queue<int>q;
q.push(t);dis[t]=0;vis[t]=1;
while(!q.empty()){
int x=q.front();q.pop();
vis[x]=0;
for(int i=first[x],v;i;i=e[i].nxt){
v=e[i].v;
if(e[i^1].f&&dis[v]<dis[x]-e[i].c){
dis[v]=dis[x]-e[i].c;
if(!vis[v]){q.push(v);vis[v]=1;}
}
}
}
return dis[s]!=-inf;
}
int dfs(int x,int f){
if(!f)return 0;
if(x==t){vis[0]=1;return f;}
vis[x]=1;
int used=0;
for(int &i=cur[x],v,w;i;i=e[i].nxt){
v=e[i].v;
if(vis[v]||!e[i].f||dis[v]+e[i].c!=dis[x])continue;
w=dfs(v,min(f,e[i].f));
if(!w)continue;
e[i].f-=w;e[i^1].f+=w;
f-=w;used+=w;
if(!f)break;
}
return used;
}
inline int mcmf(){
int flow,ret=0,cost=0;
while(spfa()){
memcpy(cur,first,sizeof(first));
vis[0]=1;
while(vis[0]){
memset(vis,0,sizeof(vis));
flow=dfs(s,inf);
if(cost+flow*dis[s]>=0){
cost+=flow*dis[s];
ret+=flow;
}
else return ret+cost/(-dis[s]);
}
}
return ret;
}
signed main(){
init();
n=read();s=n+1;t=s+1;
for(int i=1,x;i<=n;i++){
a[i]=x=read();
for(int j=1;j<=tot&&pri[j]*pri[j]<=a[i];j++)
while(x%pri[j]==0){
x/=pri[j];
cn[i]++;
}
if(x>1)cn[i]++;
}
for(int i=1;i<=n;i++){
b[i]=read();
if(cn[i]&1)add(s,i,0,b[i]);
else add(i,t,0,b[i]);
}
for(int i=1;i<=n;i++)c[i]=read();
for(int i=1;i<=n;i++){
if(!(cn[i]&1))continue;
for(int j=1;j<=n;j++){
if(cn[j]&1)continue;
if((a[i]%a[j]==0&&cn[i]==cn[j]+1)||(a[j]%a[i]==0&&cn[i]==cn[j]-1))
add(i,j,c[i]*c[j],min(b[i],b[j]));
}
}
cout<<mcmf();
return (0-0);
}