【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<=100000000),依次表示多边形的顶点坐标。多边形不一定是凸的,但是不自交——任何两条边都只有最多一个公共点——他们的公共端点。此外,没有两条连续的边平行。
【Output】
你的程序应该输出正好t行,第k行包含了一个整数nk——表示第k个多边形有多少个对称轴。
【Sample Input】
2
12
1 -1
2 -1
2 1
1 1
1 2
-1 2
-1 1
-2 1
-2 -1
-1 -1
-1 -2
1 -2
6
-1 1
-2 0
-1 -1
1 -1
2 0
1 1
【Sample Output】
4
2
【Hint】
【Solution】
没想到对称轴可以用KMP求,看了题解才知道QAQ。用长度和有向面积(即角度)表示多边形,从1号点绕到n号点一圈。这样如果从某个顶点(或边)向两个方向扩展都是相同的(即出现回文串),那么这个顶角的角平分线(或边的中垂线)即为对称轴。比较是否相同时用KMP统计即可。
代码如下:
/**************************************************************
Problem: 1100
User: llgyc
Language: Pascal
Result: Accepted
Time:4844 ms
Memory:15868 kb
****************************************************************/
const maxn = 400400; inf = maxlongint shr 1;
type point = record x,y:int64; end;
operator - (a,b:point) c:point; begin c.x:=a.x-b.x; c.y:=a.y-b.y; end;
function len2(a:point):int64; begin exit(a.x*a.x+a.y*a.y); end;
function cross(a,b:point):int64; begin exit(a.x*b.y-a.y*b.x); end;
var t,n,i,j,ans:longint;
p:array[0..maxn] of point;
a,template,pre:array[0..maxn] of int64;//a为母串,template为模式串,pre为KMP所用函数
begin
readln(t);
while (t>0) do begin
dec(t); readln(n); fillchar(a,sizeof(a),0); fillchar(template,sizeof(template),0);
for i:=1 to n do readln(p[i].x,p[i].y); p[0]:=p[n]; p[n+1]:=p[1];
for i:=1 to n do begin
a[i<<1-1]:=len2(p[i]-p[i-1]);
a[i<<1]:=cross(p[i-1]-p[i],p[i+1]-p[i]);
end;
n:=n<<1;
for i:=1 to n do begin template[n-i+1]:=a[i]; a[n+i]:=a[i]; end;
//KMP统计对称轴数
ans:=0; j:=0;
for i:=2 to n do begin
while (j<>0) and (template[i]<>template[j+1]) do j:=pre[j];
if template[i]=template[j+1] then inc(j);
pre[i]:=j;
end;
j:=0;
for i:=1 to n<<1 do begin
while (j<>0) and (a[i]<>template[j+1]) do j:=pre[j];
if a[i]=template[j+1] then inc(j);
if j=n then begin
inc(ans); j:=pre[j];
end;
end;
writeln(ans);
end;
end.