Codeforces 739E. Gosha is hunting 费用流 期望

题意

n个人,有a个p操作,b个q操作
每个操作可以使一个人以p[i]/q[i]概率激活
问激活期望

分析

首先考虑不会同时操作一个人的期望应该怎么算
拉两个点出来,表示p操作和q操作,连向人流量为1,费用为p[i]/q[i]
然后每个点表示一个人,向终点连一个流量为1,费用为0的边,然后跑一遍跑一遍费用流

现在多了同时操作一个人,考虑要减掉的贡献
1 ( 1 p [ i ] ) ( 1 q [ i ] ) p [ i ] q [ i ] = p [ i ] q [ i ]
然后每个点连一条流量为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;
}

猜你喜欢

转载自blog.csdn.net/weixin_39708759/article/details/80095825