【题解】
数形结合思考:
画出v-t图像,若一条直线在一、四象限有不被覆盖的部分,它代表的车就可以领跑
将直线按他们的斜率从小到大排序后,要维护一个下凸壳,由于一条新加入的直线,能覆盖的左边的直线是从右向左单调的,所以用单调栈来维护
需要注意的细节:
1. 要在y轴右边做半平面交,我加入了一条过原点,斜率负无穷大的直线,覆盖掉了所有直线的左边一半
2. 平行线无法算交点,重叠的直线都要算,要特殊判断
【代码】
#include<stdio.h> #include<stdlib.h> #define eps 1e-13 double b[10005],k[10005]; int num[10005],ans[10005],sta[10005]; void jh(int* a,int* b) { int t=*a; *a=*b; *b=t; } void jh(double* a,double* b) { double t=*a; *a=*b; *b=t; } void kp(int low,int high) { int i=low,j=high; double mk=k[(i+j)/2],mb=b[(i+j)/2]; while(i<j) { while( k[i]<mk || (k[i]==mk&&b[i]<mb) ) i++; while( k[j]>mk || (k[j]==mk&&b[j]>mb) ) j--; if(i<=j) { jh(&k[i],&k[j]); jh(&b[i],&b[j]); jh(&num[i],&num[j]); i++; j--; } } if(j>low) kp(low,j); if(i<high) kp(i,high); } void kpans(int low,int high) { int i=low,j=high,mid=ans[(i+j)/2]; while(i<j) { while(ans[i]<mid) i++; while(ans[j]>mid) j--; if(i<=j) { jh(&ans[i],&ans[j]); i++; j--; } } if(j>low) kpans(low,j); if(i<high) kpans(i,high); } double getx(double k1,double b1,double k2,double b2) { return (b2-b1)/(k1-k2); } int main() { int n,i,j,top=0; scanf("%d",&n); for(i=1;i<=n;i++) scanf("%lf",&b[i]); for(i=1;i<=n;i++) scanf("%lf",&k[i]); for(i=1;i<=n;i++) num[i]=i; n++;//加入直线y=0 k[n]=-1/eps; kp(1,n); for(i=1;i<=n;i++) { if(i>1&&k[i]==k[i-1]) { if(b[i]==b[i-1]) { sta[++top]=i; continue; } else { j=i-1; while(j>=1&&sta[top]==j) top--; } } while( top>1 && getx(k[i],b[i],k[sta[top]],b[sta[top]])-getx(k[sta[top-1]],b[sta[top-1]],k[sta[top]],b[sta[top]])<=-eps ) top--; sta[++top]=i; } for(i=1;i<=top;i++) ans[i]=num[sta[i]]; kpans(1,top); printf("%d\n",top-1); for(i=2;i<=top;i++) { if(ans[i]!=0) printf("%d",ans[i]); if(i<top) printf(" "); } return 0; }