一道比較NB的套路題。數組
NBA每一年都有球員選秀環節。一般用速度和身高兩項數據來衡量一個籃球運動員的基本素質。假如一支球隊裏速度最慢的球員速度爲minV,身高最矮的球員高度爲minH,那麼這支球隊的全部隊員都應該知足: A * ( height – minH ) + B * ( speed – minV ) <= C 其中A和B,C爲給定的經驗值。這個式子很容易理解,若是一個球隊的球員速度和身高差距太大,會形成配合的不協調。 請問做爲球隊管理層的你,在N名選秀球員中,最多能有多少名符合條件的候選球員。優化
第一行四個數N、A、B、C 下接N行每行兩個數描述一個球員的height和speed。spa
最多候選球員數目。指針
4 1 2 10
5 1
3 2
2 3
2 1code
4blog
1 <= N <= 5000,0<= height,speed <= 10000,A、B、C在長整型之內且爲正數。排序
最暴力的O(n^3)作法就是枚舉minH和minV,加入知足條件的點便可。ip
咱們試着優化一下:get
一看到n=5000,確定是n^2的作法,所以咱們有枚舉minH和minV其中一個的餘地,因此仍是枚舉minV,把speed[i]<minV的點去除。string
而後咱們把式子轉化一下:
因爲咱們枚舉了minV,因此minV能夠看作是一個常數,設C'=C+B*minV。
這就頗有意思,咱們設X[i]=height[i],Y[i]=A*height[i]+B*speed[i]。因而每一個運動員就對應平面直角座標系中的點(X[i],Y[i])。
當咱們枚舉minH的時候,就至關於在問有多少個點(X[i],Y[i])知足:
,這就是一個二維數點問題。
把這些點按照X[i]排序從大到小加點,用(離散化加上)樹狀數組維護Y[i],就能夠獲得一個O(n^2logn)的作法。
雖然時間複雜度爆炸可是小C纔不會告訴你小C用這個作法過了該題。
可是咱們注意到隨着minH的減少,A*minH+C'也是不斷減少的,(A>0,雖然原題沒說可是就算A爲負數也是同理的作法)。
因此咱們把這些點不只按X[i]排序,還要按Y[i]排序,用兩個指針維護,按X[i]從大到小加點,並按Y[i]從大到小刪點。
再加上咱們對這些點使用排序時用上插入排序,就能夠獲得一個O(n^2)的作法。
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define MN 5003 using namespace std; struct meg{int x,z; ll y;}a[MN]; int c1[MN],c2[MN]; bool u[MN]; int n,A,B,C,ans,tp1,tp2; inline int read() { int n=0,f=1; char c=getchar(); while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();} while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();} return n*f; } void solve(ll CX) { register int i,j,k,sum=0; ll lt; for (i=tp1,k=tp2;i;i=j) { lt=1LL*A*a[c1[i]].x+CX; for (j=i;j&&a[c1[j]].x==a[c1[i]].x;--j) if (a[c1[j]].y<=lt) ++sum,u[c1[j]]=true; for (;k&&a[c2[k]].y>lt;--k) if (u[c2[k]]) --sum,u[c2[k]]=false; ans=max(ans,sum); } for (;k;--k) if (u[c2[k]]) u[c2[k]]=false; } void isort1(int ax) { register int i,j; for (i=1;i<=tp1;++i) if (a[ax].x<a[c1[i]].x) break; ++tp1; for (j=tp1;j>i;--j) c1[j]=c1[j-1]; c1[i]=ax; } void isort2(int ax) { register int i,j; for (i=1;i<=tp2;++i) if (a[ax].y<a[c2[i]].y) break; ++tp2; for (j=tp2;j>i;--j) c2[j]=c2[j-1]; c2[i]=ax; } bool cmp1(const meg& a,const meg& b) {return a.z<b.z;} int main() { register int i,j; n=read(); A=read(); B=read(); C=read(); for (i=1;i<=n;++i) { a[i].x=read(); a[i].z=read(); a[i].y=1LL*a[i].x*A+1LL*a[i].z*B; } sort(a+1,a+n+1,cmp1); for (i=n;i;i=j) { for (j=i;j&&a[j].z==a[i].z;--j) isort1(j),isort2(j); solve(1LL*a[i].z*B+C); } printf("%d",ans); }
小C的O(n^2logn)作法(BZOJ上總時限爲3s):
O(n^2)作法(對比):
常數小就是舒服.jpg