luogu P5328 [ZJOI2019]浙江省选

传送门

每个人都可以看成一条直线\(y=ax+b\),所以我们要求的是每条线在整点处,上方线的数量的最小值(注意多条直线如果交于同一整点互不影响)

如果\(m=1\),其实只要求出半平面交,然后在半平面交上的线的答案就是\(1\).然后是\(m=2\),先把排名\(=1\)的线拿走,那么剩下的线如果排名可以为\(2\),那么至少应该在剩下的线的半平面交上.然后对于所有算过的线,他们都可以覆盖半平面交上的一段连续区间,可以使用二分求出覆盖的区间端点,然后就是找出在半平面交上的区域被上述区间覆盖的次数为1的线段更新答案,可以从左往右扫一遍求出,我这里用的是线段树.剩下的以此类推就好了

注意细节较多

// luogu-judger-enable-o2
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define db long double

using namespace std;
const int N=1e5+10;
const db eps=1e-10;
LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
struct point
{
    db x,y;
    point(){}
    point(db nx,db ny){x=nx,y=ny;}
    point operator - (const point &bb) const {return point(x-bb.x,y-bb.y);}
    db operator ^ (const point &bb) const {return x*bb.y-y*bb.x;}
};
struct line
{
    db k,b;
    int i;
    bool operator < (const line &bb) const {return fabs(k-bb.k)>eps?k<bb.k:b<bb.b;}
}a[N],p[N],st[N];
point jiao(line a,line b){db xx=(a.b-b.b)/(b.k-a.k),yy=a.k*xx+a.b;return point(xx,yy);}
db nx[N];
int n,m,an[N],tl,tp;
LL li[N][2],q[N][2],cg[N][2],bb[N<<2],mm;
int mi[N<<4],tg[N<<4],va[N];
void psup(int o){mi[o]=min(mi[o<<1],mi[o<<1|1]);}
void ad(int o,int x){mi[o]+=x,tg[o]+=x;}
void psdn(int o){if(tg[o]) ad(o<<1,tg[o]),ad(o<<1|1,tg[o]),tg[o]=0;}
void modif(int o,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr){ad(o,1);return;}
    psdn(o);
    int mid=(l+r)>>1;
    if(ll<=mid) modif(o<<1,l,mid,ll,rr);
    if(rr>mid) modif(o<<1|1,mid+1,r,ll,rr);
    psup(o);
}
int quer(int o,int l,int r,int ll,int rr)
{
    if(ll<=l&&r<=rr) return mi[o];
    psdn(o);
    int an=1<<29,mid=(l+r)>>1;
    if(ll<=mid) an=min(an,quer(o<<1,l,mid,ll,rr));
    if(rr>mid) an=min(an,quer(o<<1|1,mid+1,r,ll,rr));
    psup(o);
    return an;
}

int main()
{
    nx[0]=-1e18;
    n=rd(),m=rd();
    for(int i=1;i<=n;++i)
    {
        an[i]=-1;
        li[i][0]=rd(),li[i][1]=rd();
    }
    int nn;
    for(int h=1;h<=m;++h)
    {
        sort(p+1,p+tl+1);
        nn=0;
        for(int i=1;i<=n;++i)
            if(an[i]==-1) a[++nn]=(line){li[i][0],li[i][1],i};
        if(!nn) break;
        sort(a+1,a+nn+1);
        a[nn+1].k=-1;
        tp=0;
        for(int i=1;i<=nn;++i)
        {
            if(a[i].k==a[i+1].k) continue;
            while(tp>1&&jiao(a[i],st[tp]).y-jiao(a[i],st[tp-1]).y<-eps) --tp;
            st[++tp]=a[i];
        }
        mm=0;
        db xx=0;
        for(int i=1;i<=tp;++i)
        {
            nx[i]=i<tp?jiao(st[i],st[i+1]).x:1e18;
            q[i][0]=max((LL)(fabs(xx-floor(xx+0.5))<eps?(xx+0.5):ceil(xx)),0ll);
            q[i][1]=max((LL)(fabs(nx[i]-floor(nx[i]+0.5))<eps?(nx[i]+0.5):floor(nx[i])),-1ll);
            xx=nx[i];
            bb[++mm]=q[i][0],bb[++mm]=q[i][1];
        }
        for(int i=1;i<=tl;++i)
        {
            //if(i==67) {int Q=5e8;while(Q--);}
            int l=1,r=tp,z=tp;
            while(l<=r)
            {
                int mid=(l+r)>>1;
                if(p[i].k<=st[mid].k) z=mid,r=mid-1;
                else l=mid+1;
            }
            int zz=0;
            l=1,r=z-(p[i].k<=st[z].k);
            while(l<=r)
            {
                int mid=(l+r)>>1;
                xx=jiao(p[i],st[mid]).x;
                if(xx>nx[mid-1]-eps) zz=mid,l=mid+1;
                else r=mid-1;
            }
            if(zz)
            {
                xx=jiao(p[i],st[zz]).x;
                //if((LL)(xx+0.5)==3149034259) {int Q=5e8;while(Q--);}
                cg[i][0]=(LL)floor(xx)+1;
            }
            else cg[i][0]=0;
            zz=0;
            l=z+(p[i].k>=st[z].k),r=tp;
            while(l<=r)
            {
                int mid=(l+r)>>1;
                xx=jiao(p[i],st[mid]).x;
                if(xx<nx[mid]+eps) zz=mid,r=mid-1;
                else l=mid+1;
            }
            if(zz)
            {
                xx=jiao(p[i],st[zz]).x;
                cg[i][1]=(LL)ceil(xx)-1;
            }
            else cg[i][1]=1e18;
            bb[++mm]=cg[i][0],bb[++mm]=cg[i][1];
            //printf("%lld %lld\n",cg[i][0],cg[i][1]);
        }
        sort(bb+1,bb+mm+1),mm=unique(bb+1,bb+mm+1)-bb-1;
        nn=mm;
        for(int i=2;i<=nn;++i)
            if(bb[i]-bb[i-1]>1) bb[++mm]=(bb[i]+bb[i-1])>>1;
        sort(bb+1,bb+mm+1),mm=unique(bb+1,bb+mm+1)-bb-1;
        for(int i=0;i<=(mm<<2);++i) mi[i]=tg[i]=0;
        for(int i=1;i<=tl;++i)
        {
            cg[i][0]=lower_bound(bb+1,bb+mm+1,cg[i][0])-bb;
            cg[i][1]=lower_bound(bb+1,bb+mm+1,cg[i][1])-bb;
            modif(1,1,mm,cg[i][0],cg[i][1]);
        }
        int mx=1<<29;
        for(int i=1;i<=tp;++i)
        {
            //if(h==2&&st[i].i==75849){int Q=5e8;while(Q--);}
            q[i][0]=lower_bound(bb+1,bb+mm+1,q[i][0])-bb;
            q[i][1]=lower_bound(bb+1,bb+mm+1,q[i][1])-bb;
            va[i]=q[i][0]<=q[i][1]?quer(1,1,mm,q[i][0],q[i][1]):1<<29;
            mx=min(mx,va[i]);
            //if(va[i]<n)printf("%d\n",st[i].i);
        }
        h=mx+1;
        if(h>m) break;
        for(int i=1;i<=tp;++i)
            if(va[i]+1==h) an[st[i].i]=h,p[++tl]=st[i]/*,printf("%d %d\n",st[i].i,h)*/;
        //puts("---");
    }
    for(int i=1;i<=n;++i) printf("%d ",an[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/smyjr/p/10901945.html