ICPC 2022 KM L Light of Stars

题解

大部分的做法是,根据题意列出关于斜率的不等式,转化为二维偏序;
我是对夹角的范围进行了$ [L,\pi)-(R,\pi) $ 这样的容斥,就可以转化为两个向量的方向关系(向量积小于0),但这个做法因为只有一个方向的限制,没法保证\([0,\pi)\),所以还需要y的限制,还是二维偏序,就是少了个离散化。

主要的问题在于精度上!
对于容斥的做法而言,左右区间都是闭的,原本想的是用左闭减去右开,但这样计算闭区间的时候,共线的情况很难处理,原因在于:
1.判断共线的时候,eps很难设置。因为对于三角函数存储的误差难以估计,以及和坐标(x,y)相乘后,其误差会随着坐标的范围而改变,难以用常量来判断。
2.甚至对于开区间而言,我们对R加上一个微小量,还需要保证原本的共线情况不会被算到,即误差要大于eps,这就更加魔幻了。

然后想到把R加上一个微小量改成L减去一个微小量,计算开区间,这样可以规避上述问题;但对于R的开区间,要特别判断共线的情况,使其不计算到,这就还是有共线的问题。
在这个过程中各种调精度,正确率在20%到83%之间玄学横跳,就是真的会破防...

这时候冷静想一下,把R加上一个微小量保留,两边都是开区间且没有共线的情况,就完美地规避了共线的问题了!

对于这一类计算几何的精度问题,应该主要注意如何避免共线的判断,一般通过加减微小量改成开区间,同时可以避免共线的情况!

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const double eps=1e-9;
struct BIT{
	int n,s[N];
	void init(){
		for(int i=1;i<=n;i++) s[i]=0;
	}
	void upd(int x){
		x++;
		while(x) s[x]++,x-=x&-x;
	}
	int cnt(int x){
		x++;
		int su=0;
		while(x<=n) su+=s[x],x+=x&-x;
		return su;
	}
}B;
int n,K,ans[N];
struct node{
	int x,y,d;
	double z;
}p[N];
bool cmp(node x,node y){
	return x.z>y.z;
}
void cnt(double a,double b,int op){
	for(int i=1;i<=n;i++){
		p[i].z=a*p[i].y-b*p[i].x;
	}
	sort(p+1,p+n+1,cmp);
	B.init();
	for(int i=1;i<=n;i++){
		ans[p[i].d]+=op*B.cnt(p[i].y);
		B.upd(p[i].y);
	}
}
bool opt(node x,node y){
	return x.y>y.y;
}
int main()
{
	cin>>n>>K;
	int m=0;
	for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y),m=max(m,p[i].y),p[i].d=i;
	B.n=m+1;
	while(K--){
		double l,r;
		scanf("%lf%lf",&l,&r);
		if(l==0){
			sort(p+1,p+n+1,opt);
			for(int i=1,j=1;i<=n;i++){
				j=max(j,i);
				while(j<n && p[j+1].y==p[j].y) j++;
				ans[p[i].d]+=j-1;
			}
			r=r/180*acos(-1)+eps;
			cnt(cos(r),-sin(r),-1);
			continue;
		}
		l=l/180*acos(-1)-eps;
		r=r/180*acos(-1)+eps;
		cnt(cos(l),-sin(l),1);
		cnt(cos(r),-sin(r),-1);
	}
	for(int i=1;i<=n;i++) printf("%d ",ans[i]); puts("");
	return 0;
}
posted @ 2022-07-09 11:02  sz[sz]  阅读(61)  评论(0编辑  收藏  举报