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

NOIP2011 聪明的质监员(二分)

时间:2015-08-07 20:00:48      阅读:106      评论:0      收藏:0      [点我收藏+]

标签:二分

由于我们并不清楚要求的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);
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

NOIP2011 聪明的质监员(二分)

标签:二分

原文地址:http://blog.csdn.net/cqbzwja/article/details/47342263

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