H小明买年糕(前缀和+二分)

Description

过年了,小明准备去超市买年糕,超市里共有nn种年糕,每种年糕数量无限多,从1~n编号,每种年糕有自己的价格wiwi和美味值vivi。小明选购年糕的过程如下:

1、小明打算去买m次年糕,每次选购区间[Li, Ri][Li,Ri]内的年糕,每种年糕每次只能买一个;

2、每次选购都用事先准备好的购物袋,购物袋可以装任意数量的物品,但是这个购物袋只装价格wiwi不小于WW的年糕,区间内所有满足条件的年糕都必须要买;

3、每次出去购买年糕都会产生一个幸福值YiYi,它的计算的公式为:

Y_i=(\sum\limits_{j=L_i}^{R_i}[w_j\geq W]\times v_j)\times (\sum\limits_{j=L_i}^{R_i}[w_j\geq W])Y
i
​ =(
j=L
i


R
i

​ [w
j
​ ≥W]×v
j
​ )×(
j=L
i


R
i

​ [w
j
​ ≥W]),其中符号[条件]的意思是:若条件为真,值就是1,否则值就是0;

所以小明m次选购年糕行动所产生的总幸福值为:H=Y_1+Y_2+…+Y_mH=Y
1
​ +Y
2
​ +…+Y
m
​ ;

小明是一个精简持家的程序员,所以他选购年糕有一个标准值SS。他想通过调整事先准备的购物袋的WW值,让年糕总幸福值尽量接近SS,即使得S-HS−H的绝对值最小。现在请你帮忙调整购物袋的WW值,使得|S-H|∣S−H∣绝对值最小,并输出这个最小值。

20190220213730804.jpeg

Input

第一行包括三个整数nn, mm, SS,分别表示年糕的种类,购物的次数和标准值。

接下来nn行,每行两个整数w_iw
i
​ , v_iv
i
​ ,中间用空格隔开,表示第ii个年糕的价格w_iw
i
​ 和美味值v_iv
i
​ 。

接下来mm行,每行两个整数L_iL
i
​ , R_iR
i
​ ,中间用空格隔开,表示第ii次购物选购区间[L_i, R_i][L
i
​ ,R
i
​ ]的年糕。

1\leq n,m\leq 200000, 1\leq wi,vi\leq 1e6, 1\leq S\leq 1e12, 1\leq Li\leq Ri\leq n1≤n,m≤200000,1≤wi,vi≤1e6,1≤S≤1e12,1≤Li≤Ri≤n

Output

一个整数,表示所求最小值。

Sample Input 1

5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
Sample Output 1

10
Hint

当WW为4时,三次购物的幸福值分别为20,5,020,5,0,年糕的幸福值为2525,此时与标准值SS相差为1010,这是最优情况.

Source

2019年集训队选拔赛

思路:前缀的差分和构成二分的判断条件,是洛谷上一道原题。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>

using namespace std;
const int maxn=200010;
int w[maxn],v[maxn],l[maxn],r[maxn];
long long p_n[maxn],p_v[maxn];
long long Y,s,sum;
int n,m;
long long mx=-1,mn=1e18;

long long Max(long long a,long long b)
{
	return a>b?a:b;
}
long long Min(long long a,long long b)
{
	return a<b?a:b;
}
bool check(int W)
{   
	Y=0,sum=0;
	memset(p_n,0,sizeof(p_n));
	memset(p_v,0,sizeof(p_v));
	for(int i=1;i<=n;i++)
	{
		if(w[i]>=W) p_n[i]=p_n[i-1]+1,p_v[i]=p_v[i-1]+v[i];
		else 
			p_n[i]=p_n[i-1],p_v[i]=p_v[i-1];
	}
	for(int i=1;i<=m;i++)
		Y+=(p_n[r[i]]-p_n[l[i]-1])*(p_v[r[i]]-p_v[l[i]-1]);

	sum=llabs(Y-s);
	if(Y>s) 
		return true;
	else 
		return false;

}
int main()
{
	scanf("%d %d %lld",&n,&m,&s); 
	for(int i=1;i<=n;i++)
	{
		scanf(" %d %d",&w[i],&v[i]);
		mx=Max(mx,w[i]);
		mn=Min(mn,w[i]);    
	}
	for(int i=1;i<=m;i++)
		scanf(" %d %d",&l[i],&r[i]);
	int left=mn-1,right=mx+2,mid;  
	long long ans=999999999999999;
	while(left<=right)
	{
		mid=(left+right)>>1;
		if(check(mid))  left=mid+1;
		else right=mid-1;
		if(sum<ans) ans=sum;
	}
	printf("%lld",ans);
	return 0;
}

posted on 2019-03-27 22:19  tomjobs  阅读(183)  评论(0编辑  收藏  举报