洛谷 P5328 [ZJOI2019]浙江省选
https://www.luogu.com.cn/problem/P5328
Tutorial
https://www.luogu.com.cn/blog/foreverlasting/solution-p5328
每个选手可以看作一条(y=a_ix+b_i)的直线,他的最好名次就是找到一个非负整数(x),使得此时严格在它上面的直线数+1最小.
那么,如果想要找到所有名次=1的选手,那么做一次半平面交,即可得到那些选手,注意由于(x)只能是非负整数,所以需要在弹出需要的是考虑那条直线是否有整点在轮廓上,需要向上取整和向下取整的操作,所以本题就用分数储存浮点数.
在那之后,如果我们将所有名次=1的选手的直线删去,那么名次=2的选手也可以做一次半平面交,但注意此时轮廓上的不一定是名次=2的,因为对于某个(x),可能有多个名次=1在它之上.
可以对于此时所有已经有名次的直线,二分计算它在当前轮廓线上方的(x)的范围,然后通过差分就可以得到当前(x)有多少个直线在其上方.
之后就枚举轮廓线上的每一条直线,如果它的范围中存在(x)使在其上方直线数=当前名次数-1,那么它的最好名次就是当前名次
Code
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fi first
#define se second
using namespace std;
inline char gc() {
// return getchar();
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void rd(T &x) {
x=0; int f=1,ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
x*=f;
}
typedef long long ll;
const ll INF=2e18;
const int maxn=1e5+50;
int n,m,an[maxn];
struct Line {
int k,id; ll b;
bool operator <(const Line &other) const {
if(k!=other.k) return k<other.k;
return b>other.b;
}
} L[maxn];
struct Frac {
ll a,b,c;
Frac() {a=b=0,c=1;}
Frac(ll x,ll y) {
if(y<0) x=-x,y=-y;
a=x/y,b=x%y,c=y;
if(b<0) b+=y,--a;
}
inline bool operator <(const Frac &other) const {
if(a!=other.a) return a<other.a;
return b*other.c<other.b*c;
}
friend inline bool operator <=(const Frac &a,const Frac &b) {return !(b<a);}
inline ll fl() {return a;}
inline ll cl() {return a+bool(b);}
};
inline Frac crosP(Line u,Line v) {
return Frac(u.b-v.b,v.k-u.k);
}
void sol(int now) {
static Line q[maxn]; static Frac p[maxn]; int top=0;
for(int i=1,j=-1;i<=n;++i) if(an[L[i].id]==-1) {
if(top&&L[i].k==q[top].k) continue;
while(top&&crosP(q[top],L[i]).fl()<p[top].cl()) --top;
q[++top]=L[i];
if(top>1) p[top]=crosP(q[top],q[top-1]);
}
p[top+1]=Frac(INF,1);
vector< pair<ll,int> > tag;
for(int i=1;i<=n;++i) if(an[L[i].id]!=-1) {
{
int l=1,r=top,re=-1;
while(l<=r) {
int mid=(l+r)>>1;
if(q[mid].k>=L[i].k||crosP(L[i],q[mid])<=p[mid+1]) re=mid,r=mid-1;
else l=mid+1;
}
if(re!=-1&&q[re].k<L[i].k) tag.push_back(make_pair(crosP(L[i],q[re]).fl()+1,1));
else tag.push_back(make_pair(0,1));
}
{
int l=1,r=top,re=-1;
while(l<=r) {
int mid=(l+r)>>1;
if(q[mid].k<=L[i].k||p[mid]<=crosP(L[i],q[mid])) re=mid,l=mid+1;
else r=mid-1;
}
if(re!=-1&&q[re].k>L[i].k) tag.push_back(make_pair(crosP(L[i],q[re]).cl(),-1));
}
}
sort(tag.begin(),tag.end());
for(int i=1,k=0,cnt=0;i<=top;++i) {
while(k<tag.size()&&tag[k].fi<=p[i].cl()) cnt+=tag[k++].se;
if(cnt==now-1) an[q[i].id]=now;
while(k<tag.size()&&tag[k].fi<=p[i+1].fl()) {
int j=k; while(j<tag.size()&&tag[j].fi==tag[k].fi) cnt+=tag[j++].se;
if(cnt==now-1) an[q[i].id]=now;
k=j;
}
}
}
int main() {
rd(n),rd(m);
for(int i=1;i<=n;++i) {
rd(L[i].k),rd(L[i].b);
L[i].id=i;
}
sort(L+1,L+n+1);
memset(an,-1,sizeof(an));
for(int i=1;i<=m;++i) sol(i);
for(int i=1;i<=n;++i) {
if(i!=1) printf(" ");
printf("%d",an[i]);
}
printf("
");
return 0;
}