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

本文介绍了一种结合快速查找(三分法)与前缀和的算法,用于解决特定区间内元素数量与元素和乘积的问题。通过排序、三分法枚举及前缀和优化,实现了在O(n)时间内高效计算复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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

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

那么我们就到了第二个问题:计算对于每一个 wiw_iwi 的答案。如果每一个区间都去枚举复杂度是 n2n^2n2,肯定会 TLETLETLE 。所以我们就需要在 O(n)O(n)O(n) 的时间内处理出每一个 [Li,Ri][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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值