洛谷 P5328 [ZJOI2019]浙江省选

洛谷 P5328 [ZJOI2019]浙江省选

https://www.luogu.com.cn/problem/P5328

UErYM4.png

UErwIx.png

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("\n");
	return 0;
} 
posted @ 2020-07-08 10:36  LJZ_C  阅读(183)  评论(0编辑  收藏  举报