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