题目链接
首先一个很显然的想法就是直接\(DP\)

\(f[i][j][k]\)表示抓前\(i\)个神奇宝贝用了\(j\)个宝贝球和\(k\)个超级球,有:

\[f[i][j][k] = max (f[i - 1][j - 1][k] + p[i], f[i - 1][j][k - 1] + u[i], f[i - 1][j - 1][k - 1] + p[i] + u[i] -p[i] * u[i]) \]

这样直接转移是\(O(n^3)\)的。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define R register
#define LL long long
const int inf = 0x3f3f3f3f;
const int N = 200 + 5;

inline int read() {
	char a = getchar(); int x = 0,f = 1;
	for(;a > '9' || a < '0';a = getchar()) if(a == '-') f = -1;
	for(;a >= '0' && a <= '9'; a = getchar()) x = x * 10 + a - '0';
	return x * f;
}

int n, a, b;
double p[N], u[N];
double f[N][N][N];

int main() {
	freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	n = read(); a = read(); b = read();
	for(R int i = 1; i <= n; i ++) scanf("%lf", & p[i]);
	for(R int i = 1; i <= n; i ++) scanf("%lf", & u[i]);
	for(R int i = 1; i <= n; i ++) {
		for(R int j = 0; j <= a; j ++)
			for(R int k = 0; k <= b; k ++) {
				f[i][j][k] = f[i - 1][j][k];
				if(j != 0) f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k] + p[i]);
				if(k != 0) f[i][j][k] = max(f[i][j][k], f[i - 1][j][k - 1] + u[i]);
				if(j != 0 && k != 0) f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k - 1] + p[i] + u[i] - p[i] * u[i]);
			}
	}
	printf("%lf\n", f[n][a][b]);
	return 0;	
}

 

考虑优化,打表或者脑补可以发现,当\(n, b\)固定的时候\(f[n][a][i]\)是一个下凸的函数,而且最低点是\(f[n][a][0]\),其实脑补一下也就知道用的球越多,这个期望肯定越大,所以就有这么个性质。考虑用wqs二分进行优化。对于每一个超级球都带上一个费用,通过二分这个费用来控制宝贝球的使用个数,可以做到\(n^2log_2n\) (大概的,实际二分多少次看过了就行)。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define R register
#define LL long long
const int inf = 0x3f3f3f3f;
const int N = 2000 + 5;

inline int read() {
	char a = getchar(); int x = 0,f = 1;
	for(;a > '9' || a < '0';a = getchar()) if(a == '-') f = -1;
	for(;a >= '0' && a <= '9'; a = getchar()) x = x * 10 + a - '0';
	return x * f;
}

int n, a, b;
double p[N], u[N];
double f[N][N];
int cnt[N][N];

inline bool check(double mid) {
	memset(f, 0, sizeof(f));
	memset(cnt, 0, sizeof(cnt));
	for(R int i = 1; i <= n; i ++) {
		for(R int j = 0; j <= a; j ++) {
			cnt[i][j] = cnt[i - 1][j];
			f[i][j] = f[i - 1][j];
			if(j != 0 && f[i - 1][j - 1] + p[i] >= f[i][j]) {
				f[i][j] = f[i - 1][j - 1] + p[i];
				cnt[i][j] = cnt[i - 1][j - 1];
			}
			if(j != 0 && f[i - 1][j - 1] + u[i] + p[i] - u[i] * p[i] - mid >= f[i][j]) {
				f[i][j] = f[i - 1][j - 1] + u[i] + p[i] - u[i] * p[i] - mid;
				cnt[i][j] = cnt[i - 1][j - 1] + 1;
			}
			if(f[i - 1][j] + u[i] - mid >= f[i][j]) {
				f[i][j] = f[i - 1][j] + u[i] - mid;
				cnt[i][j] = cnt[i - 1][j] + 1;
			}
		}
	}
	return cnt[n][a] <= b;
}


int main() {
	freopen("a.in","r",stdin);
	freopen("b.out","w",stdout);
	n = read(); a = read(); b = read();
	for(R int i = 1; i <= n; i ++) scanf("%lf", & p[i]);
	for(R int i = 1; i <= n; i ++) scanf("%lf", & u[i]);
	double l = 0, r = 1;
	for(R int i = 1; i <= 60; i ++) {
		double mid = (l + r) / 2;
		if(check(mid)) r = mid;
		else l = mid;
	}
	printf("%.5lf\n", f[n][a] + l * b);
	return 0;	
}

但是实际上发现,不仅仅对于\(b\)\(a\)也有这个性质,所以可以再套一个\(wqs\),二分一个宝贝球的费用,可以做到\(nlog_2n^2\)这个也是假的

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define R register
#define LL long long
const int inf = 0x3f3f3f3f;
const int N = 2000 + 5;
const double eps = 1e-9;

inline int read() {
	char a = getchar(); int x = 0,f = 1;
	for(;a > '9' || a < '0';a = getchar()) if(a == '-') f = -1;
	for(;a >= '0' && a <= '9'; a = getchar()) x = x * 10 + a - '0';
	return x * f;

}

int n, a, b;
double p[N], u[N];
double f[N];
int cnt1[N], cnt2[N];

inline bool Check(double v1,double v2) {
	memset(f, 0, sizeof(f));
	memset(cnt1, 0, sizeof(cnt1));
	memset(cnt2, 0, sizeof(cnt2));
	for(R int i = 1;i <= n; i ++) {
		f[i] = f[i - 1];
		cnt1[i] = cnt1[i - 1];
		cnt2[i] = cnt2[i - 1];
		if(f[i - 1] + p[i] - v1 - eps > f[i]) {
			f[i] = f[i - 1] + p[i] - v1;
			cnt1[i] = cnt1[i - 1] + 1;
			cnt2[i] = cnt2[i - 1];
		}
		if(f[i - 1] + u[i] - v2 - eps > f[i]) {
			f[i] = f[i - 1] + u[i] - v2;
			cnt1[i] = cnt1[i - 1];
			cnt2[i] = cnt2[i - 1] + 1;
		}
		if(f[i - 1] + p[i] + u[i] - p[i] * u[i] - v1 - v2 - eps > f[i]) {
			f[i] = f[i - 1] + p[i] + u[i] - p[i] * u[i] - v1 - v2;
			cnt1[i] = cnt1[i - 1] + 1;
			cnt2[i] = cnt2[i - 1] + 1;
		}
	}
	return cnt2[n] <= b;
}

double tl;

inline bool check(double md) {
	double l = 0, r = 1;
	for(R int i = 1; i <= 50; i ++) {
		double mid = (l + r) / 2;
		if(Check(md, mid)) r = mid;
		else l = mid;
	}
	tl = l;
	return cnt1[n] <= a;
}


int main() {
	//freopen("a.in","r",stdin);
	//freopen("b.out","w",stdout);
	n = read(); a = read(); b = read();
	for(R int i = 1; i <= n; i ++) scanf("%lf", & p[i]);
	for(R int i = 1; i <= n; i ++) scanf("%lf", & u[i]);
	double l = 0, r = 1;
	for(R int i = 1; i <= 50; i ++) {
		double mid = (l + r) / 2;
		if(check(mid)) r = mid;
		else l = mid;
	}
	//printf("%d %d\n", cnt1[n], cnt2[n]);
	printf("%.5lf\n", f[n] + l * a + tl * b);
	return 0;	
}

但是这个写法有一个巨大的坑点就是会被卡精度,一定要在代码中减去\(eps\),这是因为我们要在判断的时候尽量少使用球,这样才可以碰到最优解。要是用了和没有用没有区别的话,不用显然会更加优秀,也是题目所要求的。