• [NOIP2011提高组day2]-2-聪明的质监员


    2.聪明的质监员(qc.cpp/c/pas) 
    【问题描述】 
    小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1
    到 n 逐一编号,每个矿石都有自己的重量 wi 以及价值 vi。检验矿产的流程是: 
    1、给定 m个区间[Li,Ri]; 
    2、选出一个参数 W; 
    3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值 Yi  : 
    ::点击图片在新窗口中打开:: j∈[Li ,Ri ]且wj ≥W ,j 是矿石编号 

    这批矿产的检验结果 Y为各个区间的检验值之和。即: ::点击图片在新窗口中打开:: 

    若这批矿产的检验结果与所给标准值 S 相差太多,就需要再去检验另一批矿产。小 T
    不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让检验结果尽可能的靠近
    标准值 S,即使得 S-Y的绝对值最小。请你帮忙求出这个最小值。 

    【输入】 
    输入文件 qc.in。 
    第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。 
    接下来的 n行,每行 2个整数,中间用空格隔开,第 i+1 行表示 i 号矿石的重量 wi 和价
    值 vi  。 
    接下来的 m行,表示区间,每行 2个整数,中间用空格隔开,第 i+n+1 行表示区间[Li, 
    Ri]的两个端点 Li 和 Ri。注意:不同区间可能重合或相互重叠。 
    【输出】 
    输出文件名为 qc.out。 
    输出只有一行,包含一个整数,表示所求的最小值。 

    【输入输出样例】 
    qc.in 
    5 3 15  
    1 5   
    2 5 
    3 5 
    4 5 
    5 5 
    1 5 
    2 4 
    3 3 
    qc.out 
    10 

    【输入输出样例说明】 
    当 W 选 4 的时候,三个区间上检验值分别为 20、5、0,这批矿产的检验结果为 25,此
    时与标准值 S相差最小为 10。 
    【数据范围】 
    对于 10%的数据,有 1≤n,m≤10; 
    对于 30%的数据,有 1≤n,m≤500; 
    对于 50%的数据,有 1≤n,m≤5,000; 
    对于 70%的数据,有 1≤n,m≤10,000; 
    对于 100%的数据,有 1≤n,m≤200,000,0<wi, vi≤10^6,0<S≤10^12,1≤Li≤Ri≤n。

    题解:很容易发现W越大,Y越小,具有单调性,可以用二分来计算;

    这里求区间内的大于W的个数乘wj大于W的石头的v的和的积,这玩意是可以用前缀和优化的;

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<iomanip>
    #include<map>
    #include<set>
    #include<vector>
    #include<ctime>
    #include<cmath>
    #define LL long long 
    using namespace std;
    #define LL long long 
    #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++)
    #define max(x,y) ((x)<(y)?(y):(x))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define FILE "1"
    namespace OI{
        const int maxn=202000;
        const LL inf=1000000000000000000LL;
        int w[maxn],v[maxn],q[maxn];
        LL ws[maxn],vs[maxn],S;
        int n,m,cnt=1;
        int b[maxn][2];
        void init(){
            freopen("1.in","r",stdin);
            freopen("1.out","w",stdout);
            scanf("%d%d%I64d",&n,&m,&S);
            up(i,1,n)scanf("%d%d",&w[i],&v[i]);
            up(i,1,m)scanf("%d%d",&b[i][0],&b[i][1]);
            up(i,1,n)q[++cnt]=w[i];q[++cnt]=10000000;
            sort(q+1,q+cnt+1);
        }
        LL ans=inf;
        bool get(int mid){
            memset(vs,0,sizeof vs);
            memset(ws,0,sizeof ws);
            up(i,1,n){
                vs[i]=vs[i-1];ws[i]=ws[i-1];
                if(w[i]>=q[mid])vs[i]+=v[i],ws[i]+=1;
            }
            LL Y=0;
            up(i,1,m){
                Y+=(ws[b[i][1]]-ws[b[i][0]-1])*(vs[b[i][1]]-vs[b[i][0]-1]);
                if(Y<0)return 1;//有可能爆LL,爆掉时,很显然不可能是最优解,直接退出即可;
            }
            if(abs(Y-S)<=ans)ans=abs(Y-S);
            return Y>=S;
        }
        void slove(){
            init();
            int left=1,right=cnt,mid;
            while(left+1<right){
                mid=(left+right)>>1;
                if(get(mid))left=mid;
                else right=mid;
            }
            get(left);get(right);
            cout<<ans<<endl;
        }
    }
    
    int main(){
        using namespace OI;
        slove();
    }
    View Code
  • 相关阅读:
    minimum-path-sum
    pascals-triangle
    Java -- 二分查找
    redis缓存雪崩,击穿,穿透(copy)
    使用redis限制提交次数
    数据库的悲观锁和乐观锁
    mysql常用命令
    php压缩Zip文件和文件打包下载
    php去除数据库的数据空格
    php获取本年、本月、本周时间戳和日期格式的实例代码(分析)
  • 原文地址:https://www.cnblogs.com/chadinblog/p/5939963.html
Copyright © 2020-2023  润新知