NOIP2011 聪明的质监员(二分)

由于我们并不清楚要求的W的值,但是我们知道W的值不超过矿石中价值最大的,如果W大于了矿石中价值最大的,那么Y的值为0,无法达到最优解。

因此,很容易就能想到在确定W的值要用二分的方法。

在分析这道题的时候,我们很容易知道Y的值是满足单调性的,当W的值越大,Y的值越小,因为W越大,能够选的矿石就越少。

所以我们把得到的Y值作为判断条件,如果Y比S小,就说明检验值了,而W取大了。每次更改W的同时给ans取最小值。

那么Y又应该怎么求出呢?题目中n,m最大有2*10^5,如果暴搜肯定超时,因此我们需要在枚举W的时候预处理。遍历w数组,保存满足条件的v和个数的前缀和。然后再遍历一遍要求的区间,即sum += ( cnt[R[i]] - cnt[L[i]-1] )*( sumv[R[i]] - sumv[L[i]] ) 之后,总的时间复杂度就从O(m*n*log(maxw))变成了O((m+n)*log(maxw))

注意此题要开long long保存

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 200005
#define LL long long
using namespace std;
LL n,m;
LL s;
LL sum;
LL w[MAXN],v[MAXN];
LL cnt[MAXN],sumv[MAXN];//保存前缀和
LL ops[MAXN][2];
void pre(int limit)//limit为当前枚举的W
{
	memset(cnt,0,sizeof cnt);
	memset(sumv,0,sizeof sumv);
	sum = 0;
	for(int i = 1; i <= n; i++)
	{
		cnt[i] = cnt[i-1];
		sumv[i] = sumv[i-1];
		if(w[i] >= limit)
		{
			cnt[i]++;
			sumv[i] += v[i];
		}
	}
	for(int i = 1; i <= m; i++)
		sum += (cnt[ops[i][1]] - cnt[ops[i][0]-1])*(sumv[ops[i][1]] - sumv[ops[i][0]-1]);
}
LL myabs(LL x)
{
	return x>0?x:-x;
}
int main()
{
	//freopen("qc14.in","r",stdin);
	//freopen("qc.out","w",stdout);
	LL L = 1,R = 0;
	scanf("%lld%lld%lld",&n,&m,&s);
	for(int i = 1; i <= n; i++)
	{
		scanf("%lld%lld",&w[i],&v[i]);
		if(w[i] > R)
			R = w[i];
	}
	for(int i = 1; i <= m; i++)
	{
		scanf("%lld%lld",&ops[i][0],&ops[i][1]);
	}
	LL mid;
	LL ans = 100000000000000LL;
	while(L <= R)
	{
		mid = (L+R)/2;
		pre(mid);//预处理,并求满足当前W的检验值
		if(ans > myabs(s-sum))
			ans = myabs(s-sum);
		if(sum < s)
			R = mid-1;
		if(sum > s)
			L = mid+1;
		if(sum == s) 
			break;
	}
	printf("%lld\n",ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值