【洛谷p1314】聪明的质监员

浅浅的花香味﹌ 2021-11-14 11:12 254阅读 0赞

聪明的质监员【题目链接】

有关算法:

二分答案;

但是你只二分答案是不够的,因为你check会炸,所以还要考虑前缀和;

首先假装我们的check已经写好了,main函数:

  1. int main() {
  2. n=read();
  3. m=read();
  4. S=read();
  5. ll maxn=0;
  6. for(ll i=1; i<=n; i++)
  7. w[i]=read(),v[i]=read(),maxn=max(maxn,w[i]);
  8. for(ll i=1; i<=m; i++)
  9. _l[i]=read(),_r[i]=read();
  10. ll l=0,r=maxn,ans1=1e17+7,ans2=1e17+7;
  11. while(l<=r) {
  12. ll mid=l+r>>1;
  13. ll ls=check(mid);
  14. if(ls<S) {
  15. ans1=min(ans1,S-ls);
  16. r=mid-1;
  17. }
  18. if(ls==S) {
  19. printf("0");
  20. return 0;
  21. }
  22. if(ls>S) {
  23. ans2=min(ans2,ls-S);
  24. l=mid+1;
  25. }
  26. }
  27. printf("%lld",min(ans1,ans2));
  28. return 0;
  29. }

输入没有什么可以说的,然后是二分答案,二分答案的话,从0~最大的wi;

二分的标准套路,先计算mid,用check函数判应该往左区间二分还是右区间二分,比较不好想的就是怎么判断往左区间还是右区间二分,这里可以想到,当我们求出的中间值的Y之后,如果发现它比S小,那么如果要找更小的差距,应该让Y的值更大才有可能,那么如果让Y的值更大,我们应该选入更多的矿产,所以我们应该使二分的答案减小,因此r=mid-1;然后这里记录两个答案,ans1,ans2,分别记录的是求得的值小于S的最小差,求得值大于S的最小差(显然等于S时就直接输出不需要再继续循环了);

然后如果没有找到使差为0的W,我们就输出ans1和ans2中较小的一个;

好了讲完了;

并没有讲完啊,我们还莫得讲check函数;

最简单的方法,暴力扫描:

  1. ll check(ll x) {
  2. ll cnt=0,sum=0,Y=0;
  3. for(ll i=1; i<=m; i++) {
  4. cnt=0;
  5. sum=0;
  6. for(ll j=_l[i]; j<=_r[i]; j++) {
  7. if(w[j]>=x) cnt++,sum+=v[j];
  8. }
  9. Y+=(cnt*sum);
  10. }
  11. return Y;
  12. }

然后你会发现你T成这样:

1605606-20190708170653695-224671428.png

然后经过大佬ych的提醒,我们想到了前缀和:

  1. ll check(ll x) {
  2. ll Y=0;
  3. for(int i=1;i<=n;i++){
  4. if(w[i]>=x) sum[i]=sum[i-1]+v[i],cnt[i]=cnt[i-1]+1;
  5. else sum[i]=sum[i-1],cnt[i]=cnt[i-1];
  6. }
  7. for(int i=1;i<=m;i++){
  8. Y+=_abs(sum[_r[i]]-sum[_l[i]-1])*_abs(cnt[_r[i]]-cnt[_l[i]-1]);
  9. }
  10. return Y;
  11. }

sum[i]表示1~i所有点中所有wi>=二分答案的的矿产的v之和,cnt[i]表示1~i以内所有点中所有wi>=二分答案的矿产个数;

然后处理应该很好理解,不再赘述;

然后再一次for循环,对于每个区间,利用维护的前缀和计算sum*cnt,然后相加即为答案;

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. inline ll read() {
  5. ll ans=0;
  6. char last=' ',ch=getchar();
  7. while(ch<'0'||ch>'9') last=ch,ch=getchar();
  8. while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
  9. if(last=='-') ans=-ans;
  10. return ans;
  11. }
  12. ll n,m,w[200001],v[200001],S,_l[200001],_r[200001],sum[200001],cnt[200001];
  13. ll _abs(ll x) {
  14. if(x<0) x=-x;
  15. return x;
  16. }
  17. ll check(ll x) {
  18. ll Y=0;
  19. for(int i=1;i<=n;i++){
  20. if(w[i]>=x) sum[i]=sum[i-1]+v[i],cnt[i]=cnt[i-1]+1;
  21. else sum[i]=sum[i-1],cnt[i]=cnt[i-1];
  22. }
  23. for(int i=1;i<=m;i++){
  24. Y+=_abs(sum[_r[i]]-sum[_l[i]-1])*_abs(cnt[_r[i]]-cnt[_l[i]-1]);
  25. }
  26. return Y;
  27. }
  28. int main() {
  29. n=read();
  30. m=read();
  31. S=read();
  32. ll maxn=0;
  33. for(ll i=1; i<=n; i++)
  34. w[i]=read(),v[i]=read(),maxn=max(maxn,w[i]);
  35. for(ll i=1; i<=m; i++)
  36. _l[i]=read(),_r[i]=read();
  37. ll l=0,r=maxn,ans1=1e17+7,ans2=1e17+7;
  38. while(l<=r) {
  39. ll mid=l+r>>1;
  40. ll ls=check(mid);
  41. if(ls<S) {
  42. ans1=min(S-ls,ans1);
  43. r=mid-1;
  44. }
  45. if(ls==S) {
  46. printf("0");
  47. return 0;
  48. }
  49. if(ls>S) {
  50. ans2=min(ans2,ls-S);
  51. l=mid+1;
  52. }
  53. }
  54. printf("%lld",min(ans1,ans2));
  55. return 0;
  56. }

end-

转载于:https://www.cnblogs.com/zhuier-xquan/p/11152445.html

发表评论

表情:
评论列表 (有 0 条评论,254人围观)

还没有评论,来说两句吧...

相关阅读

    相关 p1164

    > P1164 小A点菜 > > 题目描述 > > uim口袋里有剩M元(M<=10000)。 > > 餐馆虽低端,但是菜品种类不少,有N种(N<=100),第i

    相关 P3382

    题目:[点击打开链接][Link 1] 题意:如题,给出一个N次函数,保证在范围\[l,r\]内存在一点x,使得\[l,x\]上单调增,\[x,r\]上单调减。试求出x

    相关 P1540——机器翻译

    题目背景 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。 题目描述 这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义