\(wqs\) 二分确实强悍,但是费用流还是好理解。
容易想到建立两个点 \(A,B\),表示宝贝球和超级球。那么首先要从 \(s\) 向 \(A,B\) 分别连一条流量为 \(a/b\),费用为 \(0\) 的边。那么从 \(A,B\) 向其他每个宝贝 \(i\) 分别连一条流量为 \(1\),费用为 \(p_i/q_i\) 的边。根据逻辑,我们只需要再从每个宝贝 \(i\) 向汇点连一条流量为 \(1\),费用为 \(0\) 的边就可以了。但是这样就会忽略掉同时选宝贝球和超级球的情况。所以还需要从每个宝贝 \(i\) 处再向汇点连一条流量为 \(1\),费用为 \(-p_iq_i\) 的边。直接跑最大费用最大流即可。
时间复杂度 \(O(n^3)\),由于费用流跑得很快,可以通过。
#include<bits/stdc++.h>
using namespace std;
const int N=2005,M=1e5+5;
int n,ma,mb,s,t,k=1,mxf,h[N],lst[N];
int to[M],nx[M],vis[N],f[M],flw[N];
double w[M],mxc,q[N],p[N],dis[N];queue<int>qu;
void add(int x,int y,int v,double cs){f[++k]=v,w[k]=cs,to[k]=y,nx[k]=h[x],h[x]=k;f[++k]=0,w[k]=-cs,to[k]=x,nx[k]=h[y],h[y]=k;
}int spfa(){while(qu.size()) qu.pop();for(int i=0;i<=n+3;i++) flw[i]=0,lst[i]=-1,dis[i]=-1e9;qu.push(s),flw[s]=1e9,dis[s]=0,vis[s]=1;while(qu.size()){int x=qu.front();qu.pop(),vis[x]=0;for(int i=h[x];i;i=nx[i]){int y=to[i],v=f[i];double cs=w[i];if(dis[y]+1e-10>=dis[x]+cs||v<1) continue;flw[y]=min(flw[x],v);dis[y]=dis[x]+cs,lst[y]=i^1;if(!vis[y]) vis[y]=1,qu.push(y);}}return lst[t]>=0;
}void MCMF(){while(spfa()){mxc+=dis[t]*flw[t],mxf+=flw[t];for(int i=t;i!=s;i=to[lst[i]])f[lst[i]]+=flw[t],f[lst[i]^1]-=flw[t];}
}int main(){scanf("%d%d%d",&n,&ma,&mb),t=n+3;for(int i=1;i<=n;i++) scanf("%lf",p+i);for(int i=1;i<=n;i++) scanf("%lf",q+i);add(s,n+1,ma,0),add(s,n+2,mb,0);for(int i=1;i<=n;i++){add(n+1,i,1,p[i]),add(n+2,i,1,q[i]);add(i,t,1,0),add(i,t,1,-p[i]*q[i]);}MCMF();printf("%.10lf",mxc);return 0;
}