【题解】洛谷1314聪明的质监员(NOIP2011)

说实话,这题其实应该放到普及+/提高。思路其实就是简单的快速查找(其实你用二分或三分均可)+前缀和。

首先我们分析一下题面的式子,其实 Y i Y_i Yi 就等于该区间内大于等于 W W W 的数字数量与大于等于 W W W 的数字之和的乘积。也就是说,我们需要将重量排序后快速查找出一个与 S S S 的答案差距最小的地方。

那么,差距最小?肯定第一个想到的就是三分。所以我们的做法就出来了(下面附上思路):

sort();
三分枚举()
{
    if(左中点的答案<右中点的答案)	左端点=左中点;
    else	右端点=右中点;
}

那么我们就到了第二个问题:计算对于每一个 w i w_i wi 的答案。如果每一个区间都去枚举复杂度是 n 2 n^2 n2,肯定会 T L E TLE TLE 。所以我们就需要在 O ( n ) O(n) O(n) 的时间内处理出每一个 [ L i , R i ] [L_i,R_i] [Li,Ri] 的答案。

既然需要求的是每一个区间的答案,那么第二个算法就出来了:前缀和。我们只需要枚举每一位上的东西,然后用前缀和扫一遍统计即可。这样单次计算复杂度就降到了 O ( n ) O(n) O(n)

最后如果三分没有枚举完,手动枚举剩下的那一点即可。

下面附上代码:

#include<iostream>
#include<algorithm>
#define N 1000005
#define abs(a) ((a)<0?-(a):(a))
#define INF 9999999999999999ll
using namespace std;
long long ans=INF,n,m,S,w[N],v[N],l[N],r[N],sum[N],sum2[N],q[N];
long long check(int W)
{
	long long now=0;
	for(int i=1;i<=n;i++)
	{
		sum[i]=sum[i-1],sum2[i]=sum2[i-1];
		if(w[i]>=W)	sum[i]+=v[i],sum2[i]++;
	}
	for(int i=1;i<=m;i++)
		now+=(long long)(sum2[r[i]]-sum2[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);
	return abs(now-S);
}
int main()
{
	cin>>n>>m>>S;q[0]=-1;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>v[i],q[i]=w[i];
	for(int i=1;i<=m;i++)
		cin>>l[i]>>r[i];
	sort(q+1,q+n+1);
	int l=1,r=n;
	while(r>=l+4)
	{
		int mid=(l+r)/2,mid2=(r+mid)/2;
		if(check(q[mid])>check(q[mid2]))	l=mid;
		else	r=mid2;
	}
	for(int i=l;i<=r;i++)
		if(check(q[i])<ans)
			ans=check(q[i]);
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值