题意:给一个 nnn 个点的多边形,求对称轴个数。
n≤105n\leq 10^5n≤105
显然对称轴一定在顶点或边的中点上。
但你 n2n^2n2 枚举完全没有一点能过的样子。
冷静分析,发现有 “中点”,“对称轴”,很自然个鬼地想到了manacher。
在边的中点插入一个点,然后复制一遍断环成链。 然后跑马拉车,扩展的时候判断是否轴对称。
设点 iii 可以扩展到 [i−pi,i+pi][i-p_i,i+p_i][i−pi,i+pi],如果扩展到整个多边形就是合法的对称轴,即 2pi+1≥2n2p_i+1\geq 2n2pi+1≥2n,pi≥np_i\geq npi≥n,并且只有第一圈的点会有贡献。一个对称轴会算两次,除以 222 就是答案。
方便实现的小trick:
- 边界的地方随机一个点就不用特判。
- 判轴对称可以算出中点,用叉积判在不在已知的对称轴上。如果对称轴未确定就设成 000 向量。但要注意本来就确定的时候要特判一下不要把它改回零向量。
- 读入的坐标都乘上 444,就可以不用 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;
}