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

小明买年糕

Description

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

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

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

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

在这里插入图片描述
其中符号[条件]的意思是:若条件为真,值就是1,否则值就是0;
所以小明m次选购年糕行动所产生的总幸福值为: H = Y 1 + Y 2 + . . . + Y m ; H=Y_1+Y_2+...+Y_m ​ ;
小明是一个精简持家的程序员,所以他选购年糕有一个标准值S。他想通过调整事先准备的购物袋的W值,让年糕总幸福值尽量接近S,即使得S−H的绝对值最小。现在请你帮忙调整购物袋的W值,使得∣S−H∣绝对值最小,并输出这个最小值。
在这里插入图片描述
Input

第一行包括三个整数n, m, S,分别表示年糕的种类,购物的次数和标准值。
n w i , v i i w i v i 接下来n行,每行两个整数w i ​ , v i ​ ,中间用空格隔开,表示第i个年糕的价格w i ​ 和美味值v i ​ 。
m L i , R i i [ L i , R i ] 接下来m行,每行两个整数L i ​ , R i ​ ,中间用空格隔开,表示第i次购物选购区[L i ​ ,R i ​ ]的年糕。
1 n , m 200000 , 1 w i , v i 1 e 6 , 1 S 1 e 12 , 1 L i R i n 1≤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


这道题,一看之下就知道要用二分了(无脑二分)但关键是每一次的区间查询都会用掉不少时间,如果这么写:

for (int i=1; i<=m; i++)
for (int j=l[i]; j<=r[i]; j++)

然后就T了。。。所以我们需要简化----(前缀和)

#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
#define mac 200050
#define debug(n) printf("%d ",n)
#define ll long long
struct st
{
    int w,v;
}food[mac];
int l[mac],r[mac],n,m;
ll d[mac],dy[mac],s;
ll ok(int x);
ll abs(ll x)
{
    return x>0?x:-x;
}
int main()
{
    scanf ("%d%d%lld",&n,&m,&s);
    for (int i=1; i<=n; i++){
        scanf ("%d%d",&food[i].w,&food[i].v);
    }
    for (int i=1; i<=m; i++){
        scanf ("%d%d",&l[i],&r[i]);
    }
    int L=0,R=1e6+10,id;
    ll ans=1e18;
    for (int i=1; i<=20; i++){
        int mid=(L+R)>>1;
        ll sum=ok(mid);
        if (abs(sum-s)<ans){      //二分答案,相差较小就更新
            ans=abs(sum-s);
            id=mid;
        }
        if (sum>s) L=mid+1;      //如果太大,说明太幸福了,W需要往上调,减少购买
        else R=mid-1;
    }
    printf ("%lld\n",ans);
    return 0;
}
ll ok(int x)
{
    ll aans=0;
    memset(d,0,sizeof(d));
    memset(dy,0,sizeof(dy));
    for (int i=1; i<=n; i++){
    	if (food[i].w>x) {
    		d[i]=d[i-1]+food[i].v;
    		dy[i]=dy[i-1]+1;
		}
    	else {
    		d[i]=d[i-1];
    		dy[i]=dy[i-1];
		}
	}                             //求大于x的前缀和
    for (int i=1; i<=m; i++){
        aans+=(d[r[i]]-d[l[i]-1])*(dy[r[i]]-dy[l[i]-1]);
    }
    return aans;
}

猜你喜欢

转载自blog.csdn.net/qq_43906000/article/details/88259241