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;
}

浙公网安备 33010602011771号