码迷,mamicode.com
首页 > 其他好文 > 详细

NOIP2011聪明的质监员题解

时间:2017-08-04 22:55:25      阅读:256      评论:0      收藏:0      [点我收藏+]

标签:表示   lld   check   中间   img   closed   lib   int   math   

631. [NOIP2011] 聪明的质监员

★★   输入文件:qc.in   输出文件:qc.out   简单对比
时间限制:1 s   内存限制:128 MB

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

 

Yi=j1×jvj, j[Li,Ri] wjW,j是矿石编号

 

这批矿产的检验结果Y为各个区间的检验值之和。即:

 

Y=i=1mYi

 

若这批矿产的

检验结果

与所给标准值 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。

  这道题一开始看到Σ还以为是数学题,果断跳过,然后发现自己错了……

  然后又以为是平衡树,然而昨天虽然刚打了平衡树却因为模板错误而没能学习区间操作,果断心虚。于是打了最暴力的暴力,强行水了5分。

  这道题最重要的有两点,第一,前缀和,第二,二分。

  先解释前缀和,这个很容易理解,就是sumv[i]为在i之前所有high不小于w的value的前缀和sumw[i]为在i之前高度不小于w的个数。

  然后就是二分,考试的时候以为是三分,之前没打过,打挂了。如果以abs(Y-S)为y轴,w为x轴,那么图像是一个绝对值函数,类似二次函数,很容易让人想到三分,然而值得注意的是Y本身是随w增大而递减的,而Y-S也是如此,我们就可以利用这个性质不断地二分w,使Y无限的去接近S,Y大于0就右移,反之左移,记得开long long。

  

技术分享
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 #include<cmath>
 8 #include<map>
 9 using namespace std;
10 int n,m;
11 long long s;
12 struct no{
13     int va;
14     int w;
15 }node[2000005];
16 struct qu{
17     int li,ri;
18 }que[2000004];
19 long long sumw[2000005],sumv[2000005];
20 long long check(int w){
21     memset(sumv,0,sizeof(sumv));
22     memset(sumw,0,sizeof(sumw));
23     for(int i=1;i<=n;i++)
24     {
25         sumw[i]=sumw[i-1];
26         sumv[i]=sumv[i-1];
27         if(node[i].w>=w)
28         {
29             sumw[i]++;
30             sumv[i]+=node[i].va;
31         }
32     }
33     long long ans=0;
34     for(int i=1;i<=m;i++)
35     {
36         ans+=(sumv[que[i].ri]-sumv[que[i].li-1])*(sumw[que[i].ri]-sumw[que[i].li-1]);
37     }
38     return ans;
39 }
40 long long minn(long long a,long long b){
41     if(a>b)return b;
42     return a;
43 }
44 long long llab(long long a){
45     if(a<0)
46     return -a;
47     return a;
48 }
49 int main(){
50     scanf("%d%d%lld",&n,&m,&s);
51     int mx=0;
52     for(int i=1;i<=n;i++)
53     {
54         scanf("%d%d",&node[i].w,&node[i].va);
55         mx=max(mx,node[i].w);
56     }
57     for(int i=1;i<=m;i++)
58         scanf("%d%d",&que[i].li,&que[i].ri);
59     int li=1,ri=mx;
60     long long ans=-1;
61     while(li<=ri)
62     {
63         int mid=(li+ri)/2;
64         long long x=check(mid);
65         if(ans==-1)ans=llab(x-s);
66         ans=minn(ans,llab(x-s));
67         if(x>s)
68             li=mid+1;
69         else
70             ri=mid-1;
71     }
72     printf("%lld\n",ans);
73     //while(1);
74     return 0;
75 }
View Code

 

NOIP2011聪明的质监员题解

标签:表示   lld   check   中间   img   closed   lib   int   math   

原文地址:http://www.cnblogs.com/liutianrui/p/7287257.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!