【NOIP2011提高组】聪明的质监员

47 篇文章 13 订阅
18 篇文章 0 订阅

题目背景

NOIP2011 DAY2 试题 2 。

题目描述

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

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

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

输入格式

第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。

接下来的 n 行,每行 2 个整数,中间用空格隔开,第 i+1 行表示 i 号矿石的重量 wi 和价值 vi 。

接下来的 m 行,表示区间,每行 2 个整数,中间用空格隔开,第 i+n+1 行表示区间 [Li,Ri] 的两个端点 Li 和 Ri。注意:不同区间可能重合或相互重叠。

输出格式

输出只有一行,包含一个整数,表示所求的最小值。

样例数据 1

输入

5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3

输出

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≤106;0<S≤1012;1≤Li≤Ri≤n。

 

解析:

       首先二分答案是明显的,二分W的值,两次二分分别求出小于S的最大值和大于S的最小值,再进行比较。

       于是问题变成对于每次二分的W,如何快速求出每个区间的答案,100%的数据范围提示我们要在O(N)的时间内求出,稍有常识的人都能看出用前缀和优化一下就行了,算出前i个数中满足条件的个数及价值之和。

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int Max=200005;
int n,m,s,l,r,mid,maxx,ans=1e18,v;
int w[Max],c[Max],sum1[Max],sum2[Max],L[Max],R[Max];

inline int get_int()
{
	int x=0,f=1;
	char c;
	for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
	if(c=='-') f=-1,c=getchar();
	for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
	return x*f;
}

inline int check(int W)
{
	int ans=0;
	for(int i=1;i<=n;i++)
	{
	  sum1[i]=sum1[i-1],sum2[i]=sum2[i-1];
	  if(w[i]>=W) sum1[i]+=c[i],sum2[i]++;
	}
	for(int i=1;i<=m;i++) ans+=(sum2[R[i]]-sum2[L[i]-1])*(sum1[R[i]]-sum1[L[i]-1]);
	return ans;
}

signed main()
{
	 n=get_int(),m=get_int(),s=get_int();
	 for(int i=1;i<=n;i++) w[i]=get_int(),c[i]=get_int(),maxx=max(maxx,w[i]);
	 for(int i=1;i<=m;i++) L[i]=get_int(),R[i]=get_int();
	 l=1,r=maxx;
	 while(l<r)
	 {
	   mid=(l+r)>>1,v=check(mid);
	   if(v<=s) ans=min(s-v,ans),r=mid;
	   else l=mid+1;
	 }
	 l=1,r=maxx;
	 while(l<r)
	 {
	  mid=(l+r+1)>>1,v=check(mid);
	  if(v>=s) ans=min(ans,v-s),l=mid;
	  else r=mid-1;
	 }
	 cout<<ans;
	 return 0;
}

 

代码:
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值