CF1195F Geometers Anonymous Club

XXIX.CF1195F Geometers Anonymous Club

闵可夫斯基和是关于两个凸包的运算,其几何意义是所有来自两个凸包内部的向量之和所构成的集合。

可以被证明的是,两个凸包的闵可夫斯基和,可以通过对两个凸包上的边按照极角大小排序后依次首尾相接得到。

回到本题。依照我们上述理论,我们尝试求出闵可夫斯基和上的顶点数。因为两条边只要倾角不同就会形成一个顶点,所以问题转换为区间不同倾角数。

于是问题就变成经典原题[SDOI2009]HH的项链,无论是莫队还是BIT均可。

需要注意的是,在判断两个向量是否倾角不同时,有两种主流方法:atan2 法或是叉积法。但是,叉积法只能判是否共线,atan2 法实测会被卡精度。

于是我们将两种方法结合,大范围用 atan2 判,小范围再用叉积判。当然也可以将所有的倾角全都化成最简分数的形式进行比较。

实际使用时需要略微卡常,因为 atan2 和叉积本身都有着不小的常数。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const double eps=1e-13;
int cmp(double x){
	if(x>eps)return 1;
	if(x<-eps)return -1;
	return 0;
}
struct Vector{
	int x,y;
	Vector(){}
	Vector(int X,int Y){x=X,y=Y;}
	friend Vector operator +(const Vector &u,const Vector &v){return Vector(u.x+v.x,u.y+v.y);}
	friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.y-v.y);}
	friend ll operator &(const Vector &u,const Vector &v){return 1ll*u.x*v.y-1ll*u.y*v.x;}//cross times
	double operator!()const{return atan2(y,x);}
	friend bool operator <(const Vector &u,const Vector &v){
		int tmp=cmp(!u-!v);
		if(tmp)return tmp==-1;
		return (u&v)<0;
	}
	void print(){printf("(%d,%d)",x,y);}
};
typedef Vector Point;
Vector read(){int x,y;scanf("%d%d",&x,&y);return Vector(x,y);}
vector<Point>u[100100];
vector<Vector>v[100100];
vector<int>w[100100];
vector<pair<int,int> >r[100100];
map<Vector,int>mp;
int res[100100],sum[100100];
int t[100100];
void ADD(int x){x++;while(x<=n)t[x]++,x+=x&-x;}
int SUM(int x){int ret=0;while(x)ret+=t[x],x-=x&-x;return ret;}
int main(){
	scanf("%d",&n);
	for(int i=1,m;i<=n;i++){
		scanf("%d",&m),sum[i]=sum[i-1]+m;
		for(int j=0;j<m;j++)u[i].push_back(read());
		for(int j=0;j<m;j++)v[i].push_back(u[i][j]-u[i][(j+1)%m]);
	}
	for(int i=1;i<=n;i++)for(auto j:v[i])w[i].push_back(mp[j]),mp[j]=i;
	scanf("%d",&m);
	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),r[y].push_back(make_pair(x,i));
	for(int i=1;i<=n;i++){
		for(auto j:w[i])ADD(j);
		for(auto j:r[i])res[j.second]=SUM(j.first)-sum[j.first-1];
	}
	for(int i=1;i<=m;i++)printf("%d\n",res[i]);
	return 0;
}

posted @ 2021-04-05 20:57  Troverld  阅读(50)  评论(0编辑  收藏  举报