1100: [POI2007]对称轴osi
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 957 Solved: 394
[Submit][Status][Discuss]
Description
FGD小朋友——一个闻名遐迩的年轻数学家——有一个小MM,yours。FGD小朋友非常喜欢他的MM,所以他很乐
意帮助他的MM做数学作业。但是,就像所有科学的容器一样,FGD的大脑拒绝不停地重复思考同样的问题。不幸的
是,yours是一个十分用功的学生,所以她不停地让FGD帮助她检查她的作业。一个阳光明媚的周末,yours的数学
老师布置了非常多的寻找多边形的对称轴的题,足够她做相当长的一段时间了。在此之前FGD已经决定去海边度过
这个难得的假期,不过他还是觉得应该帮助他的MM对付可爱的数学作业。很快地,他找到了解决方案,最好写一个
程序来帮助yours检查她的数学作业。因为FGD并非一个计算机科学家,所以他找到了他的好朋友你,请你帮助他完
成这个任务。请写一个程序:读入多边形的描述计算出每个多边形的对称轴数将计算的结果输出
Input
输入的第一行包含一个正整数t(1<=t<=10),为多边形的边数。接下来,为t个多边形的描述,每个描述的第一
行为一个正整数n(3<=n<=100000),表示了多边形的点数。然后在后面n行每行两个整数x和y(?100000000<=x, y<=1
00000000),依次表示多边形的顶点坐标。多边形不一定是凸的,但是不自交——任何两条边都只有最多一个公共
点——他们的公共端点。此外,没有两条连续的边平行。
Output
你的程序应该输出正好t行,第k行包含了一个整数nk——表示第k个多边形有多少个对称轴。
神仙题,我觉得没有题解我压根想不到,这里记录一下,当作一种现成算法,说不定以后别的题目也会用到。
一开始想到,如果两个对称轴夹角最小为X,那么一个对称轴旋转所有X的倍数都回是对称轴,所以对称轴应该是把周角等分的,类似拿两个镜子来模拟一下可以得到结论。 可惜的是这个结论这里没用到,因为找到一个对称轴还是一样的复杂度。
题解是用按顺序求出多边形的边长和每个顶点角度,按角-边-角-边……依次排列出一个串,再拷贝一份放在后面(成环的效果)形成s数组,然后给这个s数组做Manacher算法,只要Len数组的值>=n那么说明存在一个至少为2*n-1的回文串,也就是以这个点或者这条边为分割处的对称轴存在。最后答案除以2,因为对称轴有两处切割点。
也可以在s数组中找原串的倒序串的出现次数,效果是一样的。
贴一份Manacher的代码
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef long double db;
struct item
{
LL dis;
db ang;
bool edge;
inline bool operator == (const item &t) const
{
if(edge^t.edge)
return false;
if(edge)
return dis==t.dis;
else
return abs(ang-t.ang)<1E-8;
}
}s[400005];
int T,n,x[100005],y[100005],Len[400005];
void Manacher(item *s, int n)
{
int mx=0,pos=0;
for(int i=1;i<=n;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*pos-i]);
else
Len[i]=1;
while(s[i-Len[i]]==s[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)
{
mx=Len[i]+i;
pos=i;
}
}
}
LL get_Dis(int p, int q)
{
return (x[p]-x[q])*(x[p]-x[q])+(y[p]-y[q])*(y[p]-y[q]);
}
db get_Ang(int p, int q, int r)
{
LL ax=x[p]-x[q],ay=y[p]-y[q];
LL bx=x[r]-x[q],by=y[r]-y[q];
db res=asin(min(1.0L,max(-1.0L,(db)(ax*by-ay*bx)/sqrtl(ax*ax+ay*ay)/sqrtl(bx*bx+by*by))));
if(ax*bx+ay*by>0)
return res;
else if(ax*by-ay*bx>0)
return acos(-1)-res;
else
return -acos(-1)-res;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&x[i],&y[i]);
s[1].ang=get_Ang(n,1,2);
for(int i=2;i<n;i++)
s[i*2-1].ang=get_Ang(i-1,i,i+1),s[i*2-1].edge=false;
s[n*2-1].ang=get_Ang(n-1,n,1);
for(int i=1;i<n;i++)
s[i*2].dis=get_Dis(i,i+1),s[i*2].edge=true;
s[n*2].dis=get_Dis(n,1),s[n*2].edge=true;
for(int i=1;i<=n*2;i++)
s[i+n*2]=s[i];
s[0].edge=true;
s[0].dis=8E18;
s[4*n+1].ang=1E9;
s[4*n+1].edge=false;
Manacher(s,n*4);
int ans=0;
for(int i=1;i<=n*2;i++)
if(Len[i]>=n||Len[i+2*n]>=n)
++ans;
printf("%d\n",ans/2);
}
return 0;
}