[JZOJ5953] 生死之境 [CodeForces 886F] Symmetric Projections【計算幾何】
阿新 • • 發佈:2018-12-21
Description
在二維平面上給出n個格點,座標範圍在
需要統計有多少條過原點的直線,滿足所有點在這條直線上的投影成中心對稱
Solution
考慮這個對稱中心是什麼。
有結論:對稱中心一定為所有點的重心 在直線上的投影。
我們考慮一條過原點的直線,用其上的任意一個向量 來表示,點積=模長*投影,那麼全部乘以相同的模長仍然是對稱的,因此就用點積來看。
考慮任意一對點
,它們的投影關於對稱中心對稱。
那對稱中心的投影到原點距離等於
多對點的情況是一樣的,就是重心了
這樣問題就簡單許多。
列舉所有的點對,如果某個點對本身就關於重心對稱,那麼任意取直線它們都是對稱的,刪去。(一個點只能匹配一個,匹配過就不行了)
考慮剩下的點對,取這兩點中點與重心的連線,過原點作連線的垂線,只有這條直線能讓這個點對關於重心對稱。
然而直接列舉判斷還是會超時。
考慮合法的點對的條件,它一定覆蓋了至少
個點對
那麼我們只需要判斷出現了至少 次的,已經匹配過的點不能再匹配,看能否覆蓋整個點(還要判斷自己與自己對稱的情況)
這樣的直線不會超過 條,掃一遍點判斷是 的,算上排序,總的複雜度就是
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);
}
}