zoukankan      html  css  js  c++  java
  • P1314 [NOIP2011 提高组] 聪明的质监员

     数据范围只允许O(nlogn)的复杂度,由于答案显然具有单调性,使用二分,那么就要有O(n)算法来写check函数.

    起初这对于我很有迷惑性,虽然知道这些区间要用前缀和来查询,却没注意到把求前缀和的过程放到check函数里,而不是预先算出所有情况下的前缀和,就可以保证复杂度了.

    牢记计算前缀和的过程仅仅是O(n),所以这题完全可以随每次check时随用随算,这个开销并不会增大复杂度.

    long long check(long long x) {  // return y
        for (int i = 1; i <= n; i++) {
            cts[i] = cts[i - 1] + (w[i] >= x);      // (符合条件的)数量的前缀和
            vs[i] = vs[i - 1] + (w[i] >= x) * v[i];   // 价值的前缀和
        }
        long long sum = 0;
        for (int i = 1; i <= m; i++)
            sum += (cts[r[i]] - cts[l[i] - 1]) * (vs[r[i]] - vs[l[i] - 1]);
        return sum;
    }

    此外,注意要求出使|s-y|最小的y,这并不是可不可行的问题,所以check不是简单的bool返回值的函数,而是返回给定w得到的y的值,据此再进行操作.

    而即使知道了y,也只能进行有限的判断:(注意y随着w的递增而递减,假设check(w0)=y)

    若s-y>0,则y的值不应该取更小,也就是w的取值不应该大于w0,即w的右边界(含)为w0.

    若s-y<=0,则y的值不应该取更大,也就是w的取值不应该小于w0,即w的左边界(含)为w0.

    根据这些判断最终是不一定会得到一个L=R的解的.

    把限制条件设为L<R-1,则结束循环时L=R-1(也有可能是L=R),这里的R对应s-y>0边界内侧的第一个值,同时L对应s-y<=0边界内侧的第一个值.

    此时简单判断一下两者谁更优即可.

    如果想要在二分的过程中直接求解出最优解也是可行的,不过这里的二分写法不很容易适应,实现起来可能也更困难.

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cmath>
    using namespace std;
    
    int n, m, w[200010], v[200010], l[200010], r[200010];
    long long cts[200010], vs[200010];
    long long s, ans = 1e13;
    
    long long check(long long x) {  // return y
        for (int i = 1; i <= n; i++) {
            cts[i] = cts[i - 1] + (w[i] >= x);
            vs[i] = vs[i - 1] + (w[i] >= x) * v[i];
        }
        long long sum = 0;
        for (int i = 1; i <= m; i++)
            sum += (cts[r[i]] - cts[l[i] - 1]) * (vs[r[i]] - vs[l[i] - 1]);
        return sum;
    }
    
    int main() {
        scanf("%d%d%lld", &n, &m, &s);
        for (int i = 1; i <= n; i++) scanf("%d%d", w + i, v + i);
        for (int i = 1; i <= m; i++) scanf("%d%d", l + i, r + i);
    
        long long L = 0, R = 1e13;
        while (L < R - 1) {
            long long mid = L + R >> 1;
            long long y = check(mid);
            if (s - y > 0)
                R = mid;
            else
                L = mid;
        }
    
        printf("%lld
    ", min(abs(check(L) - s), abs(check(R) - s)));
    
        return 0;
    }
  • 相关阅读:
    CentOS7下Elastic Stack 5.0日志分析系统搭建
    ElasticSearch 简单入门
    简单使用packetbeat
    centos7没有安装ifconfig命令的解决方法
    CentOS系统下docker的安装与卸载
    centos7 cannot find a valid baseurl for repo base
    HP P2xxx/MSA SMI-S Provider
    Zookeeper 的学习与运用
    kafka入门:简介、使用场景、设计原理、主要配置及集群搭建(转)
    利用开源架构ELK构建分布式日志系统
  • 原文地址:https://www.cnblogs.com/Gaomez/p/14259662.html
Copyright © 2011-2022 走看看