CF739E Gosha is hunting DP+wqs二分

我是从其他博客里看到这题的,上面说做法是wqs二分套wqs二分?但是我好懒呀,只用了一个wqs二分,于是\(O(nlog^2n)\)\(O(n^2logn)\)
首先我们有一个\(O(n^3)\)的暴力\(DP\),转移好写,形式优美,但复杂度不对
该怎样发现它的凸性质呢
1.打表√
2.冷静分析一波,每一种球肯定是越多越好,于是我们先固定选择\(a\)个普通球,然后那\(b\)个大师球肯定是从大到小挑选。这样的话每多选一个,新增的收益就会下降一点,也就是说这是个上凸函数。(口胡如果假的话,就锤我吧)√
然后就可以用wqs二分干掉一维啦,设\(f[i][j]\)表示考虑到第\(i\)只精灵,已经选了\(j\)个普通球和若干个大师球时的最大期望,二分一个斜率\(mid\),每次check时转移一下\(f\)数组,拿最优决策点和\(b\)比较来调整范围
细节就是如果有多个相同的\(f\)值,取大师球最多的
代码如下:

#include <bits/stdc++.h>

using namespace std;

//暴力DP:n^3
//wqs二分:n^2logn

#define N 2000
const double eps = 1e-8; //我1e-6的精度被卡掉了。。。

int n, a, b;
double p[N+5], u[N+5], mid;
double sum;
int cnt;

bool dcmp(double x, double y) {
  return fabs(x-y) <= eps;
};

struct Data {
  double v;
  int cnt;
  bool operator < (const Data &rhs) const { //方便
    return dcmp(v, rhs.v) ? cnt < rhs.cnt : v < rhs.v;
  }
}f[N+5][N+5], opt;

Data newData(Data &d, double v, int cnt) {
  return Data{d.v+v, d.cnt+cnt};
}

void check() {
  for(int i = 1; i <= n; ++i) {
    for(int j = 0; j <= a; ++j) {
      //分类讨论转移
      //1.什么都不选
      //2.只选p
      //3.只选u
      //4.两个都选
      //取max
      f[i][j] = f[i-1][j];
      if(j >= 1) f[i][j] = max(f[i][j], newData(f[i-1][j-1], p[i], 0)); //记得减掉附加权值
      f[i][j] = max(f[i][j], newData(f[i-1][j], u[i]-mid, 1));
      if(j >= 1) f[i][j] = max(f[i][j], newData(f[i-1][j-1], p[i]+u[i]-p[i]*u[i]-mid, 1));
    }
  }
  opt = f[n][a];
}

int main() {
  scanf("%d%d%d", &n, &a, &b);
  for(int i = 1; i <= n; ++i) scanf("%lf", &p[i]);
  for(int i = 1; i <= n; ++i) scanf("%lf", &u[i]);
  double l = 0, r = 1, slope = r;
  while(fabs(r-l) > eps) { //二分斜率
    mid = (l+r)*0.5;
    check();
    if(opt.cnt >= b) l = mid, slope = mid;
    else r = mid;
  }
  mid = slope;
  check();
  printf("%.5lf\n", opt.v+b*slope);
  return 0;
}
posted @ 2019-03-22 16:27  dummyummy  阅读(320)  评论(0编辑  收藏  举报