标签:二分
由于我们并不清楚要求的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); }
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:二分
原文地址:http://blog.csdn.net/cqbzwja/article/details/47342263