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

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

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

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

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

时间复杂度O(nmlogn)

[ZJOI2019]浙江省选(半平面交)
#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

 

上一篇:[Algorithm] Area of polygon


下一篇:c# – 如何创建动态“包含或LIKE”表达式,以便与Linq一起使用OData服务