码迷,mamicode.com
首页 > 其他好文 > 详细

平面选点求区域内点的个数

时间:2015-04-21 00:04:28      阅读:262      评论:0      收藏:0      [点我收藏+]

标签:

以下摘自AC大神的。据说百度空间要关了,所以,备份一下。。。



技术分享


技术分享


现在我们定义:
s(i,j) 表示 (i -> j)的 “右边”的点的个数 
g(i,j,k) 表示以 i为中心,从j点到k点的这个角度区间内部有多少个点。

技术分享

假设 i->j->k 已经按照逆时针的顺序 
那么观察g(i, j, k) + g(j, k, i)+ g(k, i, j)+ s[i][j] + s[j][k] + s[k][i];
发现了规律 

技术分享
其中 +2, + 3表示这个区域被算了几次 
于是我们可以知道 
Ans = g(i, j, k) + g(j, k, i)+ g(k, i, j)+ s[i][j] + s[j][k] + s[k][i] - 2 * (n – 2);
O(1) 

如何求s(i,j), g(i,j,k)
(1)    枚举点,并把其他点按照与当前点的极角排序
技术分享


可以知道,由于不存在3点共线,于是每一个极角最多只可能存在1个点,并有极角差为180度的2个极角至多只有1个点。(否则3点共线,矛盾)


技术分享
容易发现(i, j) “左边”的点数是很容易得到的,只需要二分得到黑色点的位置,之后统计个数即可! 
于是(i,j) 右边的也可以得到,只需要把总的 (n  - 2)个点减去左边的即可   //我认为这里应该是n-3个点才对。

在这一步,我们用O(n * n logn)的时间求得了 S(i,j)
g(i,j,k) 的思路完全类似,复杂度也为 O(n ^2 logn)
所以本题的复杂度为 O(n^2 logn + M)

 

这种解法很巧妙了,极角排序是运用得最巧妙的地方。排序后通过二分求得一边的点数,确实利害。

 

HDU 4380

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define LL __int64
using namespace std;
#define pi acos(-1.0)
struct Node{
	double x,y;
}Nv[105],Mv[1005];
int n,m;

double ang[1005], Ntang[105][105];
int s[105][105],Right[105][105];

int binsearch(double ave){
	int l=1,r=m;
	int ans=0;
	while(l<=r){
		int mid=(l+r)/2;
		if(ang[mid]<ave){
			ans=mid,l=mid+1;
		}
		else r=mid-1;
	}
	return ans;
}



void initial(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j) continue;
			Ntang[i][j]=atan2(Nv[j].y-Nv[i].y,Nv[j].x-Nv[i].x);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			ang[j]=atan2(Mv[j].y-Nv[i].y,Mv[j].x-Nv[i].x);
		}
		sort(ang+1,ang+1+m);
		for(int j=1;j<=n;j++){
			if(j==i) continue;
			double tmp=Ntang[i][j];
			int pos1=binsearch(tmp);
			s[i][j]=pos1;		//s表示极角小于tmp的个数。因为没有三点共线,所以小于。记录下这个值使得求直线一边的点数很方便 
			if(tmp>0){
				tmp=tmp-pi;
				int pos2=binsearch(tmp);
				Right[i][j]=pos1-pos2;
			}
			else{
				tmp=tmp+pi;
				int pos2=binsearch(tmp);
				Right[i][j]=m-(pos2-pos1);
			}
		}
	}
}

int Get(int i,int j,int k){
	double ang1=Ntang[j][i];
	double ang2=Ntang[j][k];
	if(ang1>ang2){
		if(ang1-ang2<pi) return s[j][i]-s[j][k];
		else return m-(s[j][i]-s[j][k]);
	}
	else{
		if(ang2-ang1<pi) return s[j][k]-s[j][i];
		else return m-(s[j][k]-s[j][i]);
	}
}

int main(){
	int icase=0;
	while(scanf("%d%d",&n,&m)!=EOF){
		for(int i=1;i<=n;i++){
			scanf("%lf%lf",&Nv[i].x,&Nv[i].y);
		}
		for(int j=1;j<=m;j++){
			scanf("%lf%lf",&Mv[j].x,&Mv[j].y);
		}
		initial();
		int ans=0;
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				for(int k=j+1;k<=n;k++){
					int tmp=Get(i,j,k)+Get(j,k,i)+Get(k,i,j)+Right[i][j]+Right[j][k]+Right[k][i]-2*m;
					if(tmp&1) ans++;
				}
			}
		}
		printf("Case %d: %d\n",++icase,ans);
	}
	return 0;
}

  

 

HDU 4367

此处注意数列是从0开始的。具体思路是:选定i,j两点连成一条直线,然后选择直线左边的的点k,通过连线ki,kj得到一个角。那么,这个角内的直线右边的点与K连线必定是与ij相交的。右边的点是角内的点减去三角形内的点。

此处应使用降幂公式:A^X=A^(X%PHI(C)+PHI(C))modC。其中(A,C)=1,是中C是素数,所以PHI(C)=C-1.

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#define pi acos(-1.0)
#define LL __int64
using namespace std;
const LL MOD= 1000000007ll;
struct Node{
	double x,y;
}Point[205];
double ang[205],Pang[205][205];
int Right[205][205],s[205][205];
int n;
LL fib[40005];

void predo(){
	fib[0]=1;
	fib[1]=1;
	for(int i=2;i<40005;i++){
		fib[i]=(fib[i-1]+fib[i-2])%(MOD-1ll)+(MOD-1ll);
	}
}

int binsearch(double ag){
	int l=1,r=n-1;
	int ret=0;
	while(l<=r){
		int m=(l+r)/2;
		if(ang[m]<ag){
			ret=m;
			l=m+1;
		}
		else r=m-1;
	}
	return ret;
}

void initial(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j) continue;
			Pang[i][j]=atan2(Point[j].y-Point[i].y,Point[j].x-Point[i].x);
		}
	}
	int nt;
	for(int i=1;i<=n;i++){
		nt=0;
		for(int j=1;j<=n;j++){
			if(i==j) continue;
			ang[++nt]=atan2(Point[j].y-Point[i].y,Point[j].x-Point[i].x);
		}
		sort(ang+1,ang+1+nt);
		for(int j=1;j<=n;j++){
			if(i==j) continue;
			double tmp=Pang[i][j];
			int pos1=binsearch(tmp);
			s[i][j]=pos1;
			if(tmp>=0){
				tmp-=pi;
				int pos2=binsearch(tmp);
				Right[i][j]=pos1-pos2;
			}
			else{
				tmp+=pi;
				int pos2=binsearch(tmp);
				Right[i][j]=n-1-(pos2-pos1);
			}
		}
	}
}

int getS(int i,int j,int k){
	double ang1=Pang[j][i];
	double ang2=Pang[j][k];
	if(ang1>ang2){
		if(ang1-ang2<pi) return s[j][i]-s[j][k]-1;
		else{
			return n-3-(s[j][i]-s[j][k]-1);
		}
	}
	else{
		if(ang2-ang1<pi) return s[j][k]-s[j][i]-1;
		else return  n-3-(s[j][k]-s[j][i]-1);
	}
}

LL quick(LL num,LL p){
	LL ret=1ll;
	while(p){
		if(p&1ll) ret=(ret*num)%MOD;
		num=(num*num)%MOD;
		p>>=1ll;
	}
	return ret;
}

int main(){
	predo();
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=n;i++)
		scanf("%lf%lf",&Point[i].x,&Point[i].y);
		initial();
/*		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++)
			cout<<s[i][j]<<" ";
			cout<<endl; 
		}*/
		LL ans=1ll; LL num;
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				num=0;
	//			chi=i,chj=j;
				for(int k=1;k<=n;k++){
					if(k==i||k==j) continue;
					if((Point[i].x-Point[k].x)*(Point[j].y-Point[k].y)-(Point[j].x-Point[k].x)*(Point[i].y-Point[k].y)<0) continue;
					int tmp=getS(i,j,k)+getS(j,k,i)+getS(k,i,j)+Right[i][j]+Right[j][k]+Right[k][i]-2*(n-3);
					tmp=getS(j,k,i)-tmp;
		//			cout<<i<<" "<<k<<" "<<j<<" ="<<tmp<<endl;
					num+=(LL)tmp;
				}
		//		cout<<num<<endl;
				ans=(ans*(quick(num,fib[num])+1ll)%MOD)%MOD;
		//		cout<<ans<<endl;
			}
		}
		printf("%I64d\n",ans);
	}
	return 0;
}

  

平面选点求区域内点的个数

标签:

原文地址:http://www.cnblogs.com/jie-dcai/p/4443025.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!