题解-POI2007 OSI-Axes of Symmetry

Problem

bzoj1100

题意概要:给定一个简单多边形(不一定凸),求其对称轴数量

数据组数(leq 10),多边形点数(leq 10^5)

Solution

这题算是跨界算法的经jian典dan题目了吧

观察多边形对称的性质,容易发现其本质就是沿着对称轴翻折可以使多边形重合,即两侧一致

进一步发现所有边长和角度相等,可证如果给定固定的角度和边长,只能得到一种图形,所以一旦某点或某边两侧的边长角度对应相等,则一定存在一条对称轴穿过该点或该边

为了快速地得到对应相等的边和角,可以采用按照多边形的输入顺序将边角混合放进序列跑manacher,处理环的话倍增序列即可

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

inline void read(int&x){
	char c11=getchar(),ob=0;x=0;
	while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')ob=1,c11=getchar();
	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;
}

const int N=1001000;
const double eps=1e-8;

struct vec{
	int x,y;
	inline vec(){}
	inline vec(const int&X,const int&Y):x(X),y(Y){}
	inline void in(){read(x),read(y);}
	friend inline vec operator - (const vec&A,const vec&B) {return vec(A.x-B.x,A.y-B.y);}
	friend inline ll operator * (const vec&A,const vec&B) {return (ll)A.x*B.y-(ll)A.y*B.x;}
	inline double len(){return sqrt((ll)x*x+(ll)y*y);}
}p[N];

struct node{
	int sgn;double v;
	inline node(){}
	inline node(const int&SGN,const double&V):sgn(SGN),v(V){}
	friend inline bool operator == (const node&A,const node&B)
		{return A.sgn==B.sgn and fabs(A.v-B.v)<eps;}
}t[N];

inline double angle(vec A,vec B,vec C){C=C-B,B=B-A;return 1.0*(B*C)/B.len()/C.len();}

int n,f[N],e;

void init(){
	read(n);
	for(int i=1;i<=n;++i)p[i].in();
	p[0]=p[n],p[n+1]=p[1];
	e=0;
	for(int i=1;i<=n;++i){
		t[++e]=node(0,0);
		t[++e]=node(1,angle(p[i-1],p[i],p[i+1]));
		t[++e]=node(0,0);
		t[++e]=node(2,(p[i+1]-p[i]).len());
	}
	for(int i=1;i<=e;++i)t[i+e]=t[i];
	t[e=e<<1|1]=node(0,0);
}

void work(){
	int R=0,ps=0,ans=0;
	for(int i=0;i<=e;++i)f[i]=0;
	for(int i=1;i<=e;++i){
		if(i<R)f[i]=min(f[ps+ps-i],R-i);
		else f[i]=1;
		while(f[i]<i and i+f[i]<=e and t[i+f[i]]==t[i-f[i]])
			++f[i];
		if(i+f[i]>R)ps=i,R=i+f[i];
		if(f[i]-1>=n+n)++ans;
	}
	printf("%d
",ans>>1);
}

int main(){
	int T;read(T);
	while(T--)init(),work();
	return 0;
}
原文地址:https://www.cnblogs.com/penth/p/10241641.html