1. 程式人生 > >[JZOJ5953] 生死之境 [CodeForces 886F] Symmetric Projections【計算幾何】

[JZOJ5953] 生死之境 [CodeForces 886F] Symmetric Projections【計算幾何】

Description

在二維平面上給出n個格點,座標範圍在 [ 1 0 6 , 1 0

6 ] [-10^6,10^6]
需要統計有多少條過原點的直線,滿足所有點在這條直線上的投影成中心對稱

n 1000 n\leq 1000

Solution

考慮這個對稱中心是什麼。

有結論:對稱中心一定為所有點的重心 ( x n ,

y n ) ({\sum x\over n},{\sum y\over n}) 在直線上的投影。

我們考慮一條過原點的直線,用其上的任意一個向量 ( p , q ) (p,q) 來表示,點積=模長*投影,那麼全部乘以相同的模長仍然是對稱的,因此就用點積來看。

考慮任意一對點 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) ,它們的投影關於對稱中心對稱。
那對稱中心的投影到原點距離等於 x 1 p + y 1 q + x 2 p + y 2 q 2 = x 1 + x 2 2 p + y 1 + y 2 2 q {x_1p+y_1q+x_2p+y_2q\over 2}={x_1+x_2\over 2}p+{y_1+y_2\over 2}q

多對點的情況是一樣的,就是重心了

這樣問題就簡單許多。
列舉所有的點對,如果某個點對本身就關於重心對稱,那麼任意取直線它們都是對稱的,刪去。(一個點只能匹配一個,匹配過就不行了)

考慮剩下的點對,取這兩點中點與重心的連線,過原點作連線的垂線,只有這條直線能讓這個點對關於重心對稱。

然而直接列舉判斷還是會超時。
考慮合法的點對的條件,它一定覆蓋了至少 n 2 n\over 2 個點對

那麼我們只需要判斷出現了至少 n 2 n\over 2 次的,已經匹配過的點不能再匹配,看能否覆蓋整個點(還要判斷自己與自己對稱的情況)

這樣的直線不會超過 n n 條,掃一遍點判斷是 O ( n ) O(n) 的,算上排序,總的複雜度就是 O ( n 2 log ( n 2 ) ) O(n^2\log (n^2))

Tips:使用atan2(y,x)函式可以直接獲得一個向量與x軸正方向夾角,非常方便。

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 1005
using namespace std;
const double pi=3.141592653589793;
int a[N][2],n,t,bf[N];
bool bz[N];
struct node
{
	int x,y;
	double c;
	friend bool operator <(node x,node y)
	{
		return x.c<y.c;
	}
}d[N*N];
int main()
{
	int t1;
	cin>>t1;
	while(t1--)
	{
		scanf("%d",&n);
		if(n<0) {printf("-1\n");continue;}
		double mx=0,my=0;
		fo(i,1,n) scanf("%d%d",&a[i][0],&a[i][1]),mx+=a[i][0],my+=a[i][1];
		mx=mx/1.0/n,my=my/1.0/n;
		int cnt=0;
		memset(bz,0,sizeof(bz));
		memset(bf,0,sizeof(bf));
		fo(i,1,n) if(a[i][0]==mx&&a[i][1]==my) bz[i]=1,cnt++;
		fo(i,1,n)
		{
			if(!bz[i])
			fo(j,i+1,n) 
			{
				if(!bz[j]&&(a[i][0]+a[j][0])/2.0==mx&&(a[i][1]+a[j][1])/2.0==my)
				{
					bz[i]=1,bz[j]=1,cnt+=2;
				} 
			}
		}
		if(cnt==n) {printf("-1\n");continue;}
		int num=0;
		fo(i,1,n)
		{
			if(!bz[i])
			fo(j,i+1,n)
			{
				if(!bz[j])
				{
					d[++num]=(node){i,j,atan2((double)(a[i][1]+a[j][1])/2.0-my,(double)(a[i][0]+a[j][0])/2.0-mx)};
					if(d[num].c<0) d[num].c+=pi;
					if(abs(d[num].c-pi)<1e-6) d[num].c=0;
				}
			}
		}
		sort(d+1,d+num+1);
		int c=1,st=1,ans=0;
		fo(i,1,num)
		{
			if(i!=num&&abs(d[i].c-d[i+1].c)<1e-6) c++;
			else
			{
				if(c>=(n-cnt)/2)
				{
					int ct=0;
					fo(j,st,i) 
					{
						if(bf[d[j].x]!=i&&bf[d[j].y]!=i) bf[d[j].x]=bf[d[j].y]=i;
					}
					fo(j,1,n) 
					{
						if(bf[j]==i||bz[j])ct++;
						else 
						{
							double p=atan2(a[j][1]-my,a[j][0]-mx);
							if(p<0) p+=pi;
							if(abs(p-pi)<1e-6) p=0;
							if(abs(p-d[st].c)<1e-6) ct++;
						}
					}
					if(ct==n) ans++;     
				}
				st=i+1;
				c=1;
			}
		}
		printf("%d\n",ans);
	}
}