[CF793E] Gosha is hunting 题解

\(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;
}
posted @ 2025-03-26 16:21  长安一片月_22  阅读(28)  评论(0)    收藏  举报