题意:
幽幽子正在练习符卡[反魂蝶八分咲]
冥界可以抽象成一个无限大的二维平面,其中白玉楼在
(
0
,
0
)
(0,0)
(0,0),冥界上存在着
n
n
n个幽灵,第
i
i
i个幽灵位于点
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi)。
一次符卡的释放可以看做一条直线,由于幽幽子身处白玉楼,所以这条直线经过原点。幽幽子想让自己的符卡看起来尽量优美,她定义一次符卡的释放是优美的,当且仅当所有的幽灵在这条直线上的投影是中心对称的。
现在幽幽子想要知道有多少种优美的释放符卡的方式,如果每一种方法都是优美的输出
−
1
-1
−1。
数据范围:
对于
30
30
30%的数据,
n
≤
10
n\leq10
n≤10
对于
60
60
60%的数据,
n
≤
200
n\leq200
n≤200
对于
100
100
100%的数据,
n
≤
1000
,
∣
x
i
∣
,
∣
y
i
∣
≤
1
0
6
,
T
≤
3
n\leq1000,|x_i|,|y_i|\leq10^6,T\leq3
n≤1000,∣xi∣,∣yi∣≤106,T≤3
Analysis:
大力猜结论,暴力出奇迹。
最后投影出来的中心对称点,一定是原点集的重心在这条直线上的对应点。
证明一下:
我们考虑把投影表示成向量的点积形式。若当前有一向量
(
x
0
,
y
0
)
(x_0,y_0)
(x0,y0)。
我们把原点集投影其上,
(
x
,
y
)
(x,y)
(x,y)会变为
(
x
∗
x
0
,
y
∗
y
0
)
(x*x_0,y*y_0)
(x∗x0,y∗y0)。
那么在这条向量上的对称点为:
(
∑
x
∗
x
0
n
,
∑
y
∗
y
0
n
)
(\frac{\sum{x*x_0}}{n},\frac{\sum{y*y_0}}{n})
(n∑x∗x0,n∑y∗y0)。将
x
0
x_0
x0提到求和之外。
而原点集重心为:
(
∑
x
n
,
∑
y
n
)
(\frac{\sum{x}}{n},\frac{\sum{y}}{n})
(n∑x,n∑y)。刚好是它投影出来的向量,证毕。
那我们考虑让每一对对称,我们枚举一对点,它们所连线段的中点与重心连线的法线,移到原点处,即为让他们对称的直线,这比较显然。
然后发现,一条直线合法,至少要出现
n
2
\frac{n}{2}
2n次,那么这样的直线只有最多只有
n
n
n条。因为一条直线至少会有
n
2
\frac{n}{2}
2n对对称点。
我们排序后暴力判断即可。复杂度
O
(
n
2
log
(
n
2
)
)
O(n^2\log{(n^2)})
O(n2log(n2))。
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
# include<cmath>
using namespace std;
const int N = 1e3 + 5;
const int X = 1e6;
typedef double db;
const db eps = 1e-6;
const db inf = 1e7;
struct node
{
db x,y;
node operator + (node r) const
{ return (node){x + r.x,y + r.y}; }
node operator * (node r) const
{ return (node){x * r.x,y * r.y}; }
bool operator == (node r) const
{ return fabs(x - r.x) <= eps && fabs(y - r.y) <= eps; }
bool operator < (node r) const
{ return fabs(x - r.x) <= eps ? y < r.y : x < r.x; }
}p[N],s[N],z,D;
struct data
{
db k,b;
bool operator == (data r) const
{ return fabs(k - r.k) <= eps; }
bool operator < (data r) const
{ return k < r.k; }
}L[N * N];
int vis[N];
int n,T,h,tot,ans;
inline node calc(data x,data y)
{
db X = y.b / (x.k - y.k),Y = X * x.k;
return (node){X,Y};
}
inline void solve(int x)
{
data P;
if (fabs(L[x].k) > eps) P = (data){-1.0 / L[x].k,0};
P.b = z.y - z.x * P.k; node mid;
if (fabs(L[x].k) > eps && fabs(L[x].k - inf) > eps) mid = calc(L[x],P);
else if (fabs(L[x].k) <= eps) mid = (node){z.x,0};
else mid = (node){0,z.y};
for (int i = 1 ; i <= h ; ++i)
{
P.b = p[i].y - p[i].x * P.k;
if (fabs(L[x].k) > eps && fabs(L[x].k - inf) > eps) s[i] = calc(L[x],P);
else if (fabs(L[x].k) <= eps) s[i] = (node){p[i].x,0};
else s[i] = (node){0,p[i].y};
} sort(s + 1,s + h + 1);
if ((h & 1) && !(s[h / 2 + 1] == mid)) return;
for (int i = 1 ; i <= h / 2 ; ++i)
{
node now = s[i] + s[h - i + 1];
now.x /= 2.0,now.y /= 2.0;
if (!(now == mid)) return;
} ++ans;
}
int main()
{
freopen("life.in","r",stdin);
freopen("life.out","w",stdout);
scanf("%d",&T);
while (T--)
{
scanf("%d",&n); z = (node){0,0},memset(vis,0,sizeof(vis)),ans = 0;
for (int i = 1 ; i <= n ; ++i) scanf("%lf%lf",&p[i].x,&p[i].y),z = z + p[i];
z.x /= (db)n,z.y /= (db)n,h = 0;
z.x *= 2.0,z.y *= 2.0;
for (int i = 1 ; i <= n ; ++i)
for (int j = i + 1 ; j <= n ; ++j)
if ((p[i] + p[j]) == z) vis[i] = vis[j] = 2;
z.x /= 2.0,z.y /= 2.0,tot = 0;
for (int i = 1 ; i <= n ; ++i) if (!vis[i]) p[++h] = p[i];
if (h <= 1) { puts("-1"); continue; }
for (int i = 1 ; i <= h ; ++i)
for (int j = i + 1 ; j <= h ; ++j)
{
node now = p[i] + p[j];
now.x /= 2.0,now.y /= 2.0;
if (fabs(now.x - z.x) <= eps) L[++tot] = (data){0,0};
else if (fabs(now.y - z.y) <= eps) L[++tot] = (data){inf,0};
else L[++tot] = (data){-1.0 / ((now.y - z.y) / (now.x - z.x)),0};
}
sort(L + 1,L + tot + 1); int las = 1;
L[tot + 1] = (data){-inf,0};
for (int i = 1 ; i <= tot ; ++i)
if (!(L[i] == L[i + 1]))
{
if (i - las + 1 >= h / 2) solve(i);
las = i + 1;
} printf("%d\n",ans);
}
return 0;
}