这题好神……
上学期某天和ljss做了这题,前两天commonc做,结果我俩都只记得这是一道神题……看了会代码终于想起来自己当时觉着这题神就是因为用俩指针扫了俩数组一个添加一个删除还能不重不漏
我们先把数组复制一遍,同时给每个运动员一个sum值代表A*h+B*s,a数组按h排序,b数组按sum排序
按任意顺序枚举v的最小值,不妨直接在a数组里枚举
对于每次外层枚举,令l和r=0
然后在a数组里升序枚举h最小值
用r扫b数组添加球员
把题里的式子移项,可知b[r].sum<=A*minh+B*minv+C时b[r]才可能被选中
这时b[r]还要保证v>minv,同时我们不能让v过大导致h作出负贡献,当只考虑v时就爆了C的时候h就只能作出负贡献才能使r被选中,所以把h从式子里去掉再移项得v<=mins+C/B
并且因为sum满足条件且v满足条件,所以h一定是满足条件的
然后用l扫a数组去掉不合法球员,如果一个球员的h比minh小并且满足上边s的判定条件,那么就把它删掉,l的h比minh小,在之前的时候一定是比minh大的,h之前满足条件,现在不满足,v满足条件,所以所以之前sum一定也满足条件,所以可以删掉,这样不重不漏
为什么不用h>=minh判h是否没做负贡献呢?因为里层在枚举h的最小值在不断增加,如果这么判的话就会有漏的了
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<algorithm> #include<iomanip> #include<cmath> #include<queue> #include<bitset> #include<map> using namespace std; #define MAXN 5010 #define ll long long char ch,BB[1<<15],*S=BB,*T=BB; #define getc() (S==T&&(T=(S=BB)+fread(BB,1,1<<15,stdin),S==T)?0:*S++) inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } struct ply{ int h; int s; ll v; }; inline bool cmph(ply x,ply y){ return x.h<y.h; } inline bool cmpv(ply x,ply y){ return x.v<y.v; } ply sh[MAXN],sv[MAXN]; int n,ans; ll A,B,C; int main(){ int i,j,l,r,cnt; n=read(); A=read(); B=read(); C=read(); for(i=1;i<=n;i++){ sh[i].h=read(); sh[i].s=read(); sh[i].v=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); for(i=1;i<=n;i++){ l=r=0; cnt=0; ll mx=sh[i].s+C/B; for(j=1;j<=n;j++){ while(r<n&&sv[r+1].v<=A*sh[j].h+B*sh[i].s+C){ r++; if(sv[r].s>=sh[i].s&&sv[r].s<=mx){ cnt++; } } while(l<n&&sh[l+1].h<sh[j].h){ l++; if(sh[l].s>=sh[i].s&&sh[l].s<=mx){ cnt--; } } if(cnt>ans){ ans=cnt; } } } printf("%d\n",ans); return 0; }