题意
n个人,有a个p操作,b个q操作
每个操作可以使一个人以p[i]/q[i]概率激活
问激活期望
分析
首先考虑不会同时操作一个人的期望应该怎么算
拉两个点出来,表示p操作和q操作,连向人流量为1,费用为p[i]/q[i]
然后每个点表示一个人,向终点连一个流量为1,费用为0的边,然后跑一遍跑一遍费用流
现在多了同时操作一个人,考虑要减掉的贡献
然后每个点连一条流量为1,费用为-p[i]q[i]的边到终点,跑一手最大费用最大流即可
然后中间因为费用是实数,加个eps,否则可能会成环TLE
代码
#include <bits/stdc++.h>
#define bin(i) (1<<(i))
#define pb push_back
using namespace std;
typedef long long ll;
const int inf = 1e9;
const int N = 2010;
const double eps = 1e-10;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node{int x,y,next,c; double d;}edge[N*N]; int len,first[N];
void ins(int x,int y,int c,double d)
{
len++; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].d=d; edge[len].next=first[x]; first[x]=len;
len++; edge[len].x=y; edge[len].y=x; edge[len].c=0; edge[len].d=-d; edge[len].next=first[y]; first[y]=len;
}
double D[N]; int s,d;
queue<int>Q; int pre[N],pos[N]; bool v[N];
bool spfa()
{
while(!Q.empty()) Q.pop(); Q.push(s);
memset(v,0,sizeof(v)); v[s] = 1;
memset(D,-63,sizeof(D)); D[s] = 0;
while(!Q.empty())
{
int x=Q.front(); Q.pop(); v[x] = 0;
for(int k=first[x];k!=-1;k=edge[k].next)
{
int y=edge[k].y;
if(edge[k].c && D[y] + eps < D[x] + edge[k].d)
{
D[y] = D[x] + edge[k].d;
pre[y] = k;
pos[y] = x;
if(!v[y]){v[y] = 1; Q.push(y);}
}
}
}
return D[d] >= 0.0;
}
double p[N],q[N];
int main()
{
int n = read(); int a = read(); int b = read();
for(int i=1;i<=n;i++) cin>>p[i];
for(int i=1;i<=n;i++) cin>>q[i];
len = 1; memset(first,-1,sizeof(first));
s = n+1; d = n+2; int p1 = n+3; int p2 = n+4;
ins(s,p1,a,0); ins(s,p2,b,0);
for(int i=1;i<=n;i++)
{
ins(p1,i,1,p[i]); ins(p2,i,1,q[i]);
ins(i,d,1,0); ins(i,d,1,-p[i] * q[i]);
}
double ans = 0.0;
while(spfa())
{
for(int i=d;i!=s;i=pos[i]) ans += edge[pre[i]].d,edge[pre[i]].c--,edge[pre[i]^1].c++;
}
return printf("%.5f\n",ans),0;
}