CF739E Gosha is hunting

分析

\(n\le2\times10^3\),再看题意,我们直接猜测是网络流。

最大流能表示一种状态(流量),费用流能表示两种状态(流量,费用)。

而这里有两种状态:使用的球数、总共的价值期望,所以我们直接贺上费用流的板子。

我们来看两种状态分别适合流量还是费用:价值是越大越好,球数则是不能超过限定(但是观察可得所有球都被用上是最优的)。

至此很明显了,流量代表所用的球数,费用代表价值。

建图

我们建立两个虚拟点 \(A,B\) 代表两种球。

  • 超级源点向两个虚拟点分别连 \(a,b\) 的流量,\(0\) 的费用(这个流量代表球能用多少次)
  • 两个虚拟点分别向每个神奇宝贝连 \(1\) 的流量,\(p_i,u_i\) 的费用(流量代表用掉一个球,费用代表产生的价值)
  • 每个神奇宝贝向超级汇点连 \(2\) 的流量,\(0\) 的费用(流量代表允许两个球)

但是我们的最后一步是有问题的,同时使用 \(p_i,u_i\) 产生的总和应该是 \(1-(1-p_i)(1-u_i)\),相比起 \(p_i+u_i\) 多了 \(-p_iu_i\)

我们把最后一种边拆成两条

  • 一条流量是 \(1\),费用是 \(0\)
  • 一条流量是 \(1\),费用是 \(-p_iu_i\)

如果只有一个球流向这里,那么会走第一条边(最大费用最大流),两个球就会一起走,达到减去这部分价值的目的。

#include<bits/stdc++.h>
using namespace std;

using ld = long double;

#define _MCMF_

const int N = 1e4 + 5;
const int inf = 1e9;
const ld eps = 1e-7;

int n,m,k,st,ed,res; ld a[N],b[N],tot;

namespace G{
	struct edge{
		int v;
		int w; ld c;
		edge *nxt,*inv;
		bool flg;
	};
	
	edge *from[N];
	
	void adde(int u,int v,int w,ld c = 0){
		edge *E = from[u] = new(edge){v,w,c,from[u],NULL,1};
		edge *F = from[v] = new(edge){u,0,-c,from[v],NULL,0};
		E->inv = F; F->inv = E;
	}
}

#ifdef _DNMF_
namespace DNMF{
	using namespace G;
	
	std::queue<int> q;
	int dep[N];
	edge *now[N];
	
	int level(){
		while(!q.empty()) q.pop();
		q.push(st);
		memset(dep,0,sizeof dep);
		memcpy(now,from,sizeof now);
		dep[st] = 1;
		while(!q.empty()){
			int u = q.front(); q.pop();
			for(edge *e = from[u];e;e = e->nxt){
				int v = e->v,w = e->w;
				if(w && !dep[v]){
					dep[v] = dep[u] + 1;
					q.push(v);
				}
			}
		}
		return dep[ed];
	}
	
	int dfs(int u,int fl){
		if(u == ed) return res += fl,fl;
		int cur = fl;
		for(edge *e = now[u];e && cur;e = e->nxt){
			now[u] = e;
			int v = e->v,w = e->w;
			if(w && dep[v] == dep[u] + 1){
				int cfl = dfs(v,w < cur ? w : cur);
				e->w -= cfl; e->inv->w += cfl; cur -= cfl;
			}
		}
		return fl - cur;
	}
	
	void solve(){
		while(level()) dfs(st,inf);
	}
} using namespace DNMF;
#endif

#ifdef _MCMF_
namespace MCMF{
	using namespace G;
	
	std::queue<int> q;
	ld dis[N]; int vis[N];
	edge *now[N];
	
	int SPFA(){
		while(!q.empty()) q.pop();
		q.push(st);
		memset(vis,0,sizeof vis);
		fill(begin(dis),end(dis),1e9);
		memcpy(now,from,sizeof now);
		dis[st] = 0; vis[st] = 1;
		while(!q.empty()){
			int u = q.front(); q.pop();
			vis[u] = 0;
			for(edge *e = from[u];e;e = e->nxt){
				int v = e->v,w = e->w; ld c = e->c;
				if(w && dis[v] > dis[u] + c + eps){
					dis[v] = dis[u] + c;
					if(!vis[v]){
						vis[v] = 1;
						q.push(v);
					}
				}
			}
		}
		return dis[ed] != 1e9;
	}
	
	int dfs(int u,int fl){
		if(u == ed) return res += fl,fl;
		int cur = 0; vis[u] = 1;
		for(edge *e = now[u];e && (fl - cur);e = e->nxt){
			now[u] = e;
			int v = e->v,w = e->w; ld c = e->c;
			if(w && fabs(dis[v] - dis[u] - c) <= eps && !vis[v]){
				int cfl = dfs(v,w < (fl - cur) ? w : (fl - cur));
				e->w -= cfl; e->inv->w += cfl;
				cur += cfl; tot += c * cfl;
			}
		}
		return vis[u] = 0,cur;
	}
	
	void solve(){
		while(SPFA()) dfs(st,inf);
	}
} using namespace MCMF;
#endif

signed main(){
	cin >> n >> m >> k;
	int point1 = n + 1;
	int point2 = n + 2;
	st = 0,ed = n + 3;
	adde(st,point1,m,0);
	adde(st,point2,k,0);
	for(int i = 1;i <= n;++i) cin >> a[i];
	for(int i = 1;i <= n;++i) cin >> b[i];
	for(int i = 1;i <= n;++i){
		adde(point1,i,1,-a[i]);
		adde(point2,i,1,-b[i]);
		adde(i,ed,1,0);
		adde(i,ed,1,a[i] * b[i]);
	}
	solve();
	cout << double(-tot) << endl;
	return 0;
}
posted @ 2022-08-05 21:02  One_Zzz  阅读(41)  评论(0)    收藏  举报