• [wqs二分模板] CF739E Gosha is hunting


    题目链接
    首先一个很显然的想法就是直接(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
    ", 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
    ", 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
    ", cnt1[n], cnt2[n]);
    	printf("%.5lf
    ", f[n] + l * a + tl * b);
    	return 0;	
    }
    

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

  • 相关阅读:
    git提交到项目中自动部署
    vue纯前端导入导出excel
    VMware Workstation pro14 虚拟机下安装CentOS6.5图文教程
    [正睿集训2021] 杂题再讲
    #4 CF568E & CF613E & CF587D
    Atcoder Grand Contest 009&010
    [省选集训2022] 模拟赛4
    #3 CF536D & CF538G & CF559E
    [省选集训2022] 模拟赛7
    Atcoder Grand Contest 007&008
  • 原文地址:https://www.cnblogs.com/clover4/p/13086688.html
Copyright © 2020-2023  润新知