• [ZJOI2019]浙江省选(半平面交)


    一眼看上去就应该能用半平面交去做。

    首先考虑怎么求可能得第1名的人:每个人的函数为直线,就是在所有人的半平面交中的上边界者即可获得第一名,这个可以单调队列求解。

    再考虑如何求可能得第2名的人:满足2个条件:1、在去掉可能得第1名的人后可以拿第1,这个跳转到上面的过程;2、至多同时被1个能拿第一名的人超越,这个应该在半平面上做一个区间覆盖,可以线段树或者二分求解,我懒得不会写线段树就只写了二分。

    第3~m名,可以仿照第2名的过程,做m次半平面交,其实会m=2就是会正解了。

    时间复杂度O(nmlogn)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<ll,int>pii;
    const int N=1e5+7;
    struct node{
        ll a,b,c;
        node(){a=b=0,c=1;}
        node(ll x,ll y){if(y<0)x=-x,y=-y;a=x/y;b=x%y;c=y;if(b<0)b+=c,a--;}
        ll ceil(){return a+(b>0);}
        bool operator<(const node&t)const{return a==t.a?b*t.c<t.b*c:a<t.a;}
        bool operator<=(const node&t)const{return a==t.a?b*t.c<=t.b*c:a<t.a;}
    }p[N];
    int n,m,top,tot,id[N],ans[N],st[N];
    ll a[N],b[N];
    pii s[N<<1];
    bool cmp(int x,int y){return a[x]<a[y]||a[x]==a[y]&&b[x]>b[y];}
    node cross(int x,int y){return node(b[y]-b[x],a[x]-a[y]);}
    void solve(int k)
    {
        top=tot=0;
        p[0]=node(0,1);
        for(int i=1;i<=n;i++)
        if(ans[id[i]]==-1&&a[id[i]]>a[st[top]])
        {
            while(top&&cross(id[i],st[top]).a<p[top].ceil())top--;
            st[++top]=id[i];
            if(top>1)p[top]=cross(st[top-1],id[i]);
        }
        p[top+1]=node(1ll<<60,1);
        for(int i=1;i<=n;i++)
        if(ans[i]>0)
        {
            int l=1,r=top-1,mid,pos=top;
            while(l<=r)
            {
                mid=l+r>>1;
                if(a[st[mid]]>=a[i]||cross(st[mid],i)<=p[mid+1])pos=mid,r=mid-1;
                else l=mid+1;
            }
            s[++tot]=pii(a[st[pos]]>=a[i]?0:cross(st[pos],i).a+1,1);
            l=2,r=top,pos=1;
            while(l<=r)
            {
                mid=l+r>>1;
                if(a[st[mid]]<=a[i]||p[mid]<=cross(st[mid],i))pos=mid,l=mid+1;
                else r=mid-1;
            }
            if(a[st[pos]]>a[i])s[++tot]=pii(cross(st[pos],i).ceil(),-1);
        }
        sort(s+1,s+tot+1);
        for(int i=1,j=1,sum=0;i<=top;i++)
        {
            while(j<=tot&&s[j].first<=p[i].ceil())sum+=s[j++].second;
            if(sum<k)ans[st[i]]=k;
            while(j<=tot&&s[j].first<=p[i+1].a)
            {
                int p=j;
                while(p<=tot&&s[p].first==s[j].first)sum+=s[p++].second;
                if(sum<k)ans[st[i]]=k;
                j=p;
            }
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]),id[i]=i,ans[i]=-1;
        sort(id+1,id+n+1,cmp);
        for(int i=1;i<=m;i++)solve(i);
        for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    }
    View Code
  • 相关阅读:
    随手
    会使用基本的Render函数后,就会想,这怎么用 v-for/v-if/v-model;我写个vue Render函数进阶
    iframe子页面与父页面元素的访问以及js变量的访问[zhuan]
    vue element-ui 的奇怪组件el-switch
    URLSearchParams和axios的post请求(防忘记)
    ios vue2.0使用html5中的audio标签不能播放音乐
    Unity Input System教程
    关于OpenGPU.org
    Better ultra_simple for Slamtec RPLIDAR on Linux
    八字心得
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/10837038.html
Copyright © 2020-2023  润新知