洛谷P5328/LOJ3047/UOJ471/BZOJ5519[ZJOI2019]浙江省选(半平面交)
$m=1$:
直接半平面交即可。
$m=2$:
考虑删掉所有第一名以后再跑一次半平面交,显然未来的第二名一定在求出的凸壳上。但直接赋值是不对的,因为被删掉的第一可能继续占掉第二名的位置让蒟蒻没有活路,因此,用被删掉的直线截凸壳,如果凸壳某段直线存在一个$x$使得它上方只有1条被删去的直线,那么它可以成为第二名。
$m>2$:
重复过程,当前进行到第$k$轮,则检查是否只有$k-1$条直线比它大。
实现要点:
- 题目要求的$x$为非负整数,所以只要在第一象限求,并且求交点时要考虑向上/向下取整。
- $10^{18}$的规模卡精度,考虑手写$a+\frac{p}{q}$表示交点坐标,并实现取整。
复杂度$O(mnlogn)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=100050; char rB[1<<21],*rS,*rT,wB[(1<<21)+50]; int wp=-1; inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;} inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;} inline ll rd(){ char c=gc(); while(c<48||c>57)c=gc(); ll x=c&15; for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15); return x; } short buf[15]; inline void wt(int x){ if(wp>(1<<21))flush(); short l=-1; if(x<0){ wB[++wp]='-'; x=-x; } while(x>9){ buf[++l]=x%10; x/=10; } wB[++wp]=x|48; while(l>=0)wB[++wp]=buf[l--]|48; wB[++wp]=' '; } int a[N],rk[N],c[N<<1],sti[N],sl=0,s[N],n,k; ll b[N]; struct qnum{ ll a; int p,q; qnum(){} qnum(ll x,int y){ if(y<0){x=-x;y=-y;} a=x/y;p=x%y;q=y; if(p<0){--a;p+=q;} } inline bool operator <(const qnum &b)const{return a==b.a?(ll)p*b.q<(ll)b.p*q:a<b.a;} inline bool operator <=(const qnum &b)const{return !(b<*this);} inline ll ceil(){return a+(p>0);} }stp[N]; struct node{ ll p; short k; node(){} node(ll p,short k):p(p),k(k){} }tp[N<<1]; inline bool cmp1(int x,int y){return a[x]<a[y]||a[x]==a[y]&&b[x]>b[y];} inline bool cmp2(node x,node y){return x.p<y.p;} inline qnum cross(int k1,ll b1,int k2,ll b2){return qnum(b2-b1,k1-k2);} inline void work(){ int i,j=1,l,r,mid,pos,cnt=0,res=0,t; stp[sl=0]=stp[1]=qnum(0ll,1); for(i=1;i<=n;++i)if(!s[rk[i]]&&a[rk[i]]>a[sti[sl]]){ while(sl&&cross(a[rk[i]],b[rk[i]],a[sti[sl]],b[sti[sl]]).a<stp[sl].ceil())--sl; sti[++sl]=rk[i]; if(sl>1)stp[sl]=cross(a[rk[i]],b[rk[i]],a[sti[sl-1]],b[sti[sl-1]]); } stp[sl+1]=qnum(0x3f3f3f3f3f3f3f3f,1); for(i=1;i<=n;++i)if(s[i]){ l=1;r=sl;pos=0; while(l<=r){ mid=l+r>>1; if(a[sti[mid]]>=a[i]||cross(a[i],b[i],a[sti[mid]],b[sti[mid]])<=stp[mid+1]){pos=mid;r=mid-1;} else l=mid+1; } tp[++cnt]=node(a[sti[pos]]>=a[i]?0ll:cross(a[sti[pos]],b[sti[pos]],a[i],b[i]).a+1ll,1); l=1;r=sl;pos=0; while(l<=r){ mid=l+r>>1; if(a[sti[mid]]<=a[i]||stp[mid]<=cross(a[i],b[i],a[sti[mid]],b[sti[mid]])){pos=mid;l=mid+1;} else r=mid-1; } if(a[sti[pos]]>a[i])tp[++cnt]=node(cross(a[sti[pos]],b[sti[pos]],a[i],b[i]).ceil(),-1); } sort(tp+1,tp+cnt+1,cmp2); for(i=1;i<=sl;++i){ for(;j<=cnt&&tp[j].p<=stp[i].ceil();++j)res+=tp[j].k; if(res==k-1)s[sti[i]]=k; for(;j<=cnt&&tp[j].p<=stp[i+1].a;j=t){ for(t=j;t<=cnt&&tp[t].p==tp[j].p;++t)res+=tp[t].k; if(res==k-1)s[sti[i]]=k; } } } int main(){ int m,i; n=rd();m=rd(); for(i=1;i<=n;++i){a[i]=rd();b[i]=rd();rk[i]=i;} sort(rk+1,rk+n+1,cmp1); for(k=1;k<=m;++k)work(); for(i=1;i<=n;++i)wt(s[i]?s[i]:-1); flush(); return 0; }