• NOIP2011 聪明的质监员


    2聪明的质监员

    小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1到n 逐一编号,每个矿石都有自己的重量 wi 以及价值vi 。检验矿产的流程是:
    1 、给定m 个区间[Li,Ri];
    2 、选出一个参数 W;
    3 、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi:

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

     输入输出格式 Input/output

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

    【思路】

      二分+前缀和思想优化。

      要求最小的S-Y,可以看出W是需要枚举的,知W则知Y,能根据调节W的大小来调节Y的大小,用二分来完成这个任务。二分W:如果Y<S则取左区间使Y增大 如果Y>S则取右区间使Y减小,如果为0则退出。

      如果普通枚举求m个区间的Y需要O(nm)的时间。求m个区间中满足条件的个数,可以用前缀和的思想优化。

      优化:O(n)时间根据W计算出sum,sumv再需要O(m)时间计算总Y值。总体时间为O(logW max(n,m))                                                                                                                                                                                                                                                                    

    【代码】

     1 #include<iostream>
     2 #include<algorithm>
     3 using namespace std;
     4 
     5 typedef long long LL;
     6 const int maxn = 200000+10;
     7 int w[maxn],v[maxn],L[maxn],R[maxn];
     8 int n,m;
     9 LL S,ans;  //ans初值的设定 
    10 LL sum[maxn],sumv[maxn]; //前缀 
    11 void getsum(int W) {  //O(n)根据时间计算前缀 
    12     for(int i=1;i<=n;i++){
    13         sum[i]=sum[i-1];
    14         sumv[i]=sumv[i-1];
    15         if(w[i]>=W) {sum[i]++; sumv[i]+=v[i]; }
    16     }
    17 }
    18 inline LL calc(int l,int r,int W){  //O(1)返回Y 
    19      return (sum[r]-sum[l-1])*(sumv[r]-sumv[l-1]);
    20 }
    21 int main() {
    22     ios::sync_with_stdio(false);
    23     cin>>n>>m>>S;
    24     int x=0,y=-(1<<30);
    25     for(int i=1;i<=n;i++) {
    26       cin>>w[i]>>v[i]; y=max(y,w[i]);
    27     }
    28     y++;
    29     for(int i=1;i<=m;i++) cin>>L[i]>>R[i];
    30     while(x<y) {
    31         int mid=x+(y-x)/2;
    32         getsum(mid);
    33         LL Y=0;
    34         for(int i=1;i<=m;i++) Y+=calc(L[i],R[i],mid);
    35         if(ans==0) ans=abs(Y-S); else ans=min(ans,abs(Y-S)); //答案可能很大最好不设INF
    36         if(Y<S) y=mid;  // 放大Y 
    37         else if(Y>S) x=mid+1; //缩小Y 
    38         else break;    //得到ans=0 
    39     }
    40     cout<<ans;
    41     return 0;
    42 }
  • 相关阅读:
    visual studio 项目中使用EF创建的数据库,后续更新数据库操作(生产已经部署,不能删除数据库重新创建)
    使用Visual Studio 开发SharePoint项目时的快捷键
    JQuery预览图片
    Sharepoint 编辑WebPart时,WebPart属性为灰色不可用
    iWS工作流加载顺序
    dll备份注意事项
    Repeater 横向显示数据
    MDN开发者网络
    hdu 1010:Tempter of the Bone(DFS + 奇偶剪枝)
    ytu 2463:给小鼠补充代码(DFS 深度优先搜索)
  • 原文地址:https://www.cnblogs.com/lidaxin/p/4859347.html
Copyright © 2020-2023  润新知