BZOJ 1071 组队 详解(单调队列)

来源:互联网 发布:一个简单的python程序 编辑:程序博客网 时间:2024/05/22 10:58

1071: [SCOI2007]组队

Time Limit: 3 Sec Memory Limit: 128 MB

Description
NBA每年都有球员选秀环节。通常用速度和身高两项数据来衡量一个篮球运动员的基本素质。假如一支球队里
速度最慢的球员速度为minV,身高最矮的球员高度为minH,那么这支球队的所有队员都应该满足: A * ( height
– minH ) + B * ( speed – minV ) <= C 其中A和B,C为给定的经验值。这个式子很容易理解,如果一个球队的
球员速度和身高差距太大,会造成配合的不协调。 请问作为球队管理层的你,在N名选秀球员中,最多能有多少名
符合条件的候选球员。

Input
第一行四个数N、A、B、C 下接N行每行两个数描述一个球员的height和speed

Output
最多候选球员数目。

Sample Input

4 1 2 10
5 1
3 2
2 3
2 1

Sample Output

4

HINT
数据范围: N <= 5000 ,height和speed不大于10000。A、B、C在长整型以内。

思路:
一看,这不是n^3吗?再仔细想想,还是n^3呀!
绝望。。n^2log好像也过不了诶(还是有大佬卡过),n^2是正解??枚举minH,minS不就n^2了吗?难道是指针扫一遍,听着像那么回事。额,但是写不来。。%大佬
弄两个数组保存信息,同时处理每个运动员val=A * h+B * s,sh数组按h排序,sv数组按val排序。
外层在sh中枚举i(minS),内层在sh数组里升序枚举j(minH),拿l,r两个指针在sh和sv里面扫。用r扫sv数组添加球员,用l扫sh数组删减球员。
移项可知sv[r].val<=A * minH+B * minS+C时sv[r]才可能被选中
这时sv[r]还要保证s>minS,同时我们不能让v过大导致h比之前的minH小(本来就是非法状态,也保证不到l指针处理过的区间里去),所以把h从式子里去掉再移项得s<=minS+C/B。
然后用l扫sh数组删除不合法的球员,如果一个球员的h比minH小并且满足上边s的判定条件,那么就把它删掉>
为什么不在扫r指针时用h>=minH判h是否合法呢?因为在内层枚举时minH在增加,可能它对于当前的minH合法了,但是之后又不合法了。
还有一些细节和原因请参照代码注释。

#include <iostream>  #include <cstdio>  #include <cstring>  #include <algorithm>  #define LL long long  using namespace std;  const int N = 5010;  int n,ans;  LL A, B, C;  struct ER{      int h, s;      LL val;  }sh[N], sv[N]; inline bool cmph(ER aa, ER bb){      return aa.h < bb.h;  }  inline bool cmpv(ER aa, ER bb){      return aa.val < bb.val;  }  int main(){      cin >> n >> A >> B >> C;     for(int i=1; i<=n; i++){          scanf("%d%d", &sh[i].h, &sh[i].s);        sh[i].val = A * sh[i].h + B * sh[i].s;        sv[i] = sh[i];    }    sort(sh+1, sh+n+1, cmph);    sort(sv+1, sv+n+1, cmpv);    int l, r, cnt;      for(int i=1; i<=n; i++){//枚举i为speed最小的球员         l = r = 0; cnt = 0;        LL mx = sh[i].s + C / B;//超过mx,h就一定小于 sh[j].h了         for(int j=1; j<=n; j++){//枚举j为height最小的球员             while(r<n && sv[r+1].val<=A*sh[j].h+B*sh[i].s+C){//满足条件A*(height-minH)+B*(speed-minV)<=C                r++;//A*sh[j].h+B*sh[i].s+C是递增的,所以对于每一个i,r都是单调的                 if(sv[r].s>=sh[i].s && sv[r].s<=mx) cnt++;                //因为sv[r+1].val>A*sh[j-1].h+B*sh[i].s+C(终止while的条件)                //所以只要我们保证 sv[r+1].s<=mx 那么 sv[r+1].h 就一定大于sh[j-1].h                //这样的话这个球员才会在一会删除的时候考虑到             }//统计了不考虑h的合法球员。             while(l<n && sh[l+1].h<sh[j].h){                l++;//sh[j].h是递增的,所以对于每一个i,l都是单调的                 if(sh[l].s>=sh[i].s && sh[l].s<=mx/* && sh[l].val<=A*sh[j].h+B*sh[i].s+C*/) cnt--;            }//sh[l].s<=mx && sh[l].h<sh[j].h 所以A*sh[l].h+B*sh[i].s < A*sh[j].h+B*sh[i].s+C             //一定满足sh[l].val<=A*sh[j].h+B*sh[i].s+C,也就是说一定是加入过的(不重不漏)             ans = max(ans, cnt);        }    }    printf("%d\n", ans);    return 0;}