题意:给一个 n n n 个点的多边形,求对称轴个数。
n ≤ 1 0 5 n\leq 10^5 n≤105
显然对称轴一定在顶点或边的中点上。
但你 n 2 n^2 n2 枚举完全没有一点能过的样子。
冷静分析,发现有 “中点”,“对称轴”,很自然个鬼地想到了manacher。
在边的中点插入一个点,然后复制一遍断环成链。 然后跑马拉车,扩展的时候判断是否轴对称。
设点 i i i 可以扩展到 [ i − p i , i + p i ] [i-p_i,i+p_i] [i−pi,i+pi],如果扩展到整个多边形就是合法的对称轴,即 2 p i + 1 ≥ 2 n 2p_i+1\geq 2n 2pi+1≥2n, p i ≥ n p_i\geq n pi≥n,并且只有第一圈的点会有贡献。一个对称轴会算两次,除以 2 2 2 就是答案。
方便实现的小trick:
- 边界的地方随机一个点就不用特判。
- 判轴对称可以算出中点,用叉积判在不在已知的对称轴上。如果对称轴未确定就设成 0 0 0 向量。但要注意本来就确定的时候要特判一下不要把它改回零向量。
- 读入的坐标都乘上 4 4 4,就可以不用 double。
复杂度 O ( n ) O(n) O(n)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cstdlib>
#define MAXN 400005
using namespace std;
inline int read()
{
int ans=0,f=1;
char c=getchar();
while (!isdigit(c)) (c=='-')&&(f=-1),c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return f*ans;
}
typedef long long ll;
int x[MAXN],y[MAXN];
int p[MAXN],maxr,mid,cnt;
int lasx,lasy;
inline bool check(int l,int i,int r)
{
if ((ll)(x[l]-x[i])*(x[l]-x[i])+(ll)(y[l]-y[i])*(y[l]-y[i])!=(ll)(x[r]-x[i])*(x[r]-x[i])+(ll)(y[r]-y[i])*(y[r]-y[i]))
return false;
int tx=(x[l]+x[r])/2-x[i],ty=(y[l]+y[r])/2-y[i];
if ((ll)tx*lasy!=(ll)ty*lasx) return false;
if ((ll)tx*tx+(ll)ty*ty) lasx=tx,lasy=ty;
return true;
}
int main()
{
for (int T=read();T;T--)
{
int n=read();
for (int i=1;i<=2*n;i+=2) x[i]=read()*4,y[i]=read()*4;
for (int i=2*n+1;i<=4*n;i+=2) x[i]=x[i-2*n],y[i]=y[i-2*n];
x[4*n+1]=x[1],y[4*n+1]=y[1];
for (int i=2;i<=4*n;i+=2) x[i]=(x[i-1]+x[i+1])/2,y[i]=(y[i-1]+y[i+1])/2;
x[0]=rand(),y[0]=rand(),x[4*n+1]=rand(),y[4*n+1]=rand();
maxr=mid=cnt=0;
for (int i=1;i<=4*n;i++)
{
if (i<maxr) p[i]=min(p[2*mid-i],maxr-i);
else p[i]=0;
lasx=lasy=0;
while (check(i-p[i]-1,i,i+p[i]+1)) ++p[i];
if (i+p[i]>maxr) maxr=i+p[i],mid=i;
cnt+=(p[i]>=n);
}
printf("%d\n",cnt/2);
}
return 0;
}