• 「JSOI2015」送礼物


    「JSOI2015」送礼物

    传送门

    看到这题首先想到分数规划。

    我们发现对于当前区间,如果它的最大值和最小值不是分居区间的两个端点的话,那么我们显然可以把两端多出去的部分舍掉,因为,在区间最大值最小值都不变的情况下,区间肯定是越短越优的。

    但是要注意一点就是区间长度也是有下界的。

    所以说我们就先处理所有区间长度为下界 (L) 的情况,然后再对区间长度位于 ([L + 1, R]) 的区间做处理。

    二分答案 (mid) ,假设当前区间是 ([l, r]) 那么就有:

    [frac{max_{i = l}^r{a_i} - min_{i = l}^r{a_i}}{r - l + k} ge mid \Rightarrow (max_{i = l}^r{a_i} + l imes mid) - (min_{i = l}^r{a_i} + r imes mid) ge k imes mid ]

    由于我们之前说过最大值和最小值一定分居区间的两个端点(是这里不妨假设 (a_l) 为最大值,另一种情况同理

    那么就有:

    [(a_l + l imes mid) - (a_r + r imes mid) ge k imes mid ]

    所以我们可以令 (f_i = a_i + i imes mid) ,然后枚举左端点,查询右端点的最小值即可(这个可以用 ( ext{ST}) 表维护)

    但是要记得判断右端点越界的情况。

    参考代码:

    #include <algorithm>
    #include <cstdio>
    #define rg register
    #define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
    using namespace std;
    template < class T > inline void read(T& s) {
        s = 0; int f = 0; char c = getchar();
        while ('0' > c || c > '9') f |= c == '-', c = getchar();
        while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
        s = f ? -s : s;
    }
     
    const int _ = 5e4 + 5;
    const double eps = 1e-7;
     
    int n, k, L, R, a[_], lg[_];
    int mn[18][_], mx[18][_]; double f[18][_];
     
    inline int query_mn(int l, int r) {
        int x = lg[r - l + 1];
        return min(mn[x][l], mn[x][r - (1 << x) + 1]);
    }
     
    inline int query_mx(int l, int r) {
        int x = lg[r - l + 1];
        return max(mx[x][l], mx[x][r - (1 << x) + 1]);
    }
     
    inline double query(int l, int r) {
        int x = lg[r - l + 1];
        return min(f[x][l], f[x][r - (1 << x) + 1]);
    }
     
    inline bool chk(double mid) {
        for (rg int i = 1; i <= n; ++i) f[0][i] = a[i] + mid * i;
        for (rg int i = 1; i <= lg[n]; ++i)
        	for (rg int j = 1; j + (1 << i) - 1 <= n; ++j)
    	        f[i][j] = min(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
        double res = -2e9;
        for (rg int i = 1; i + L <= n; ++i)
        	res = max(res, f[0][i] - query(i + L, min(i + R - 1, n)));
        return res >= k * mid;
    }
     
    inline bool check(double mid) {
        if (chk(mid)) return 1;
        reverse(a + 1, a + n + 1);
        if (chk(mid)) return 1;
        return 0;
    }
     
    inline void solve() {
        read(n), read(k), read(L), read(R);
        for (rg int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;
        for (rg int i = 1; i <= n; ++i) read(a[i]), mn[0][i] = mx[0][i] = a[i];
        for (rg int i = 1; i <= lg[n]; ++i)
        	for (rg int j = 1; j + (1 << i) - 1 <= n; ++j) {
    	        mn[i][j] = min(mn[i - 1][j], mn[i - 1][j + (1 << (i - 1))]);
    	        mx[i][j] = max(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]);
        }
        double ans = -2e9;
        for (rg int i = 1; i + L - 1 <= n; ++i)
        ans = max(ans, 1.0 * (query_mx(i, i + L - 1) - query_mn(i, i + L - 1)) / (L + k - 1));
        double l = 0, r = 1000;
        while (r - l > eps) {
        	double mid = (l + r) / 2;
        	if (check(mid)) l = mid; else r = mid;
        }
        printf("%.4lf
    ", max(ans, l));
    }
     
    int main() {
    #ifndef ONLINE_JUDGE
        file("cpp");
    #endif
        int T; read(T);
        while (T--) solve();
        return 0;
    }
    
  • 相关阅读:
    实验四 Linux系统搭建C语言编程环境
    实验三 Linux系统用户管理及VIM配置
    实验二 Linux系统简单文件操作命令
    《Linux命令行与shell脚本编程大全》勘错
    考研英语每天一段阅读理解
    仓储管理系统500bug记录一下mysql 8小时超时解决办法
    win7 64位4GB内存下 tomcat7扩大内存
    解决远程连接mysql错误1130代码的方法
    win7 64 位 tomcat 定时重启脚本
    通过Navicat for MySQL远程连接的时候报错mysql 1130
  • 原文地址:https://www.cnblogs.com/zsbzsb/p/12301860.html
Copyright © 2020-2023  润新知