bzoj4514 [Sdoi2016]数字配对
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
2 4 8
2 200 7
-1 -2 1
Sample Output
4
HINT
n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5
正解:费用流。
这题的费用流模型还是比较显然的,不过有两个要注意的地方。
首先这题需要建成二分图的模型,所以每个点的流量肯定会乘$2$,如果直接连可能会导致有些点多用了流量。对于这种情况,我们在每个$i->j$的连边时,把$j->i$也连边,最后把流量除以$2$,就能解决这个问题了。
还有一个问题,题目是问的费用$>=0$的最大流,首先我们肯定要把费用取反,转成最小费用最大流。然后我们可以在每次增广时加一个特判,如果之前增广的费用+当前费用$>0$,那么我们直接取使得费用$<=0$的最大流量就行了。因为费用流每次都是找最短路增广,所以这样做是对的。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define inf (1LL<<60) 15 #define N (3010) 16 #define il inline 17 #define RG register 18 #define ll long long 19 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 20 21 using namespace std; 22 23 struct edge{ ll nt,to,flow,cap,dis; }g[200010]; 24 25 ll head[N],dis[N],vis[N],f[N],p[N],fa[N],a[N],b[N],c[N]; 26 ll q[5000010],n,S,T,flow,cost,num=1; 27 28 il ll gi(){ 29 RG ll x=0,q=1; RG char ch=getchar(); 30 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 31 if (ch=='-') q=-1,ch=getchar(); 32 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 33 return q*x; 34 } 35 36 il void insert(RG ll from,RG ll to,RG ll cap,RG ll cost){ 37 g[++num]=(edge){head[from],to,0,cap,cost},head[from]=num; return; 38 } 39 40 il ll bfs(RG ll S,RG ll T){ 41 for (RG ll i=1;i<=T;++i) dis[i]=inf; 42 RG ll h=0,t=1; q[t]=S,dis[S]=0,vis[S]=1,f[S]=inf; 43 while (h<t){ 44 RG ll x=q[++h],v; 45 for (RG ll i=head[x];i;i=g[i].nt){ 46 v=g[i].to; 47 if (dis[v]>dis[x]+g[i].dis && g[i].cap>g[i].flow){ 48 dis[v]=dis[x]+g[i].dis,fa[v]=x,p[v]=i; 49 f[v]=min(f[x],g[i].cap-g[i].flow); 50 if (!vis[v]) vis[v]=1,q[++t]=v; 51 } 52 } 53 vis[x]=0; 54 } 55 if (dis[T]==inf) return 0; 56 if (cost+dis[T]*f[T]>0){ //费用>0特判 57 RG ll x=-cost/dis[T]; 58 flow+=x; return 0; 59 } 60 flow+=f[T],cost+=dis[T]*f[T]; 61 for (RG ll i=T;i!=S;i=fa[i]) 62 g[p[i]].flow+=f[T],g[p[i]^1].flow-=f[T]; 63 return 1; 64 } 65 66 il ll isprime(RG ll x){ 67 if (x==0 || x==1) return 0; 68 if (!(x&1)) return x==2; 69 for (RG ll i=2;i*i<=x;++i) 70 if (!(x%i)) return 0; 71 return 1; 72 } 73 74 il void work(){ 75 n=gi(),S=2*n+1,T=2*n+2; 76 for (RG ll i=1;i<=n;++i) a[i]=gi(); 77 for (RG ll i=1;i<=n;++i) b[i]=gi(); 78 for (RG ll i=1;i<=n;++i) c[i]=gi(); 79 for (RG ll i=1;i<=n;++i){ 80 insert(S,i,b[i],0),insert(i,S,0,0); 81 insert(n+i,T,b[i],0),insert(T,n+i,0,0); 82 } 83 for (RG ll i=1;i<=n;++i) 84 for (RG ll j=1;j<=n;++j){ 85 if (a[i]%a[j]) continue; 86 if (isprime(a[i]/a[j])){ 87 insert(i,n+j,inf,-c[i]*c[j]),insert(n+j,i,0,c[i]*c[j]); 88 insert(j,n+i,inf,-c[i]*c[j]),insert(n+i,j,0,c[i]*c[j]); 89 //防止多余流量影响结果 90 } 91 } 92 while (bfs(S,T)); printf("%lld\n",flow>>1); return; 93 } 94 95 int main(){ 96 File("match"); 97 work(); 98 return 0; 99 }