【POI2007】OSI-Axes of Symmetry【计算几何】【manacher】

题意:给一个 n n n 个点的多边形,求对称轴个数。

n ≤ 1 0 5 n\leq 10^5 n105

显然对称轴一定在顶点或边的中点上。

但你 n 2 n^2 n2 枚举完全没有一点能过的样子。

冷静分析,发现有 “中点”,“对称轴”,很自然个鬼地想到了manacher。

在边的中点插入一个点,然后复制一遍断环成链。 然后跑马拉车,扩展的时候判断是否轴对称。

设点 i i i 可以扩展到 [ i − p i , i + p i ] [i-p_i,i+p_i] [ipi,i+pi],如果扩展到整个多边形就是合法的对称轴,即 2 p i + 1 ≥ 2 n 2p_i+1\geq 2n 2pi+12n p i ≥ n p_i\geq n pin,并且只有第一圈的点会有贡献。一个对称轴会算两次,除以 2 2 2 就是答案。

方便实现的小trick:

  1. 边界的地方随机一个点就不用特判。
  2. 判轴对称可以算出中点,用叉积判在不在已知的对称轴上。如果对称轴未确定就设成 0 0 0 向量。但要注意本来就确定的时候要特判一下不要把它改回零向量。
  3. 读入的坐标都乘上 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值