洛谷P5328/LOJ3047/UOJ471/BZOJ5519[ZJOI2019]浙江省选(半平面交)

$m=1$:

直接半平面交即可。

$m=2$:

考虑删掉所有第一名以后再跑一次半平面交,显然未来的第二名一定在求出的凸壳上。但直接赋值是不对的,因为被删掉的第一可能继续占掉第二名的位置让蒟蒻没有活路,因此,用被删掉的直线截凸壳,如果凸壳某段直线存在一个$x$使得它上方只有1条被删去的直线,那么它可以成为第二名。

$m>2$:

重复过程,当前进行到第$k$轮,则检查是否只有$k-1$条直线比它大。

实现要点:

  1. 题目要求的$x$为非负整数,所以只要在第一象限求,并且求交点时要考虑向上/向下取整。
  2. $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;
}
View Code

 

posted @ 2019-08-23 16:24  wangyuchen  阅读(191)  评论(0编辑  收藏  举报