版权属于ZYXZYXZYX,想要引用此题(包括题面)的朋友请联系博主
题目来源:[POI 2007] Axes of Symmetry
分析:
可能是数据较弱
所以用计算几何A掉了
(然而翔哥把我的程序放到原题网站上,一样A掉了。。。果然我是打暴力的料)
方法一
对于多边形的对称轴,我们要分类讨论:
- n为偶数
- 点与点之间的连线形成的对称轴:
我们枚举多边形上的点 A A ,因为对称轴两边的点数量一定相等,所以我们可以直接找到在多边形上的对称点 B B
得到,暴力判断是否点对都关于 line(A,B) l i n e ( A , B ) 对称 - 边的中垂线形成的对称轴:
我们枚举多边形上的一条边 AB A B ,得到 AB A B 的中垂线 l l ,暴力判断是否点对都关于对称
- 点与点之间的连线形成的对称轴:
- n为奇数
- 点与点之间的连线形成的对称轴:
因为对称轴两边的点数量一定相等,所以这样的对称轴不会存在 - 边的中垂线形成的对称轴:
我们枚举多边形上的一条边 AB A B ,得到 AB A B 的中垂线 l l ,暴力判断是否点对都关于对称
- 点与点之间的连线形成的对称轴:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const double eps=1e-8;
struct node{
double x,y;
node (double xx=0,double yy=0) {
x=xx;y=yy;
}
};
node p[150000];
struct node2{
double a,b,c;
};
int n,ans;
node operator +(const node &a,const node &b) {return node(a.x+b.x,a.y+b.y);}
node operator -(const node &a,const node &b) {return node(a.x-b.x,a.y-b.y);}
node operator *(const node &a,const double &b) {return node(a.x*b,a.y*b);}
node operator /(const node &a,const double &b) {return node(a.x/b,a.y/b);}
node2 get(node A,node B) {
node2 ans;
ans.a=B.y-A.y;
ans.b=A.x-B.x;
ans.c=A.y*B.x-A.x*B.y;
return ans;
}
bool ON(node A,node2 L) {
double t=A.x*L.a+A.y*L.b+L.c;
if (fabs(t)<eps) return 1;
else return 0;
}
double dis(node A,node2 L) {
double t=fabs(A.x*L.a+A.y*L.b+L.c);
double tt=sqrt(L.a*L.a+L.b*L.b);
return t/tt;
}
bool DC(node A,node B,node2 L) {
node2 L1=get(A,B);
if (fabs(L.a*L1.a+L.b*L1.b)>eps) return 0;
double t1=dis(A,L),t2=dis(B,L);
if (fabs(t1-t2)<eps) return 1;
else return 0;
}
void edge_cnt() {
int tot=0;
for (int i=0;i<n;i++) {
node A=p[i];
node B=p[(i+1)%n];
node C=(A+B)/2;
node2 L=get(A,B);
swap(L.a,L.b); L.b=-L.b;
L.c=-L.a*C.x-L.b*C.y;
int ll=(i+1)%n,rr=i%n;
int ff=1;
for (int j=1;j<=n/2+1;j++) {
if (ll==rr) {
if (!ON(p[ll],L)) {
ff=0;
break;
}
}
else {
if (!DC(p[ll],p[rr],L)) {
ff=0;
break;
}
ll=(ll+1)%n;
rr=(rr-1+n)%n;
}
}
tot+=ff;
}
if (n%2==0) tot/=2; //如果点数是偶数,那么同一条会计算两次
ans+=tot;
}
void point_cnt() {
for (int i=0;i<n/2;i++) {
node A=p[i];
node B=p[(i+n/2)%n];
node2 L=get(A,B);
int ll=(i+1)%n,rr=(i-1+n)%n;
int ff=1;
for (int j=1;j<=n/2+1;j++) {
if (!DC(p[ll],p[rr],L)) {
ff=0;
break;
}
ll=(ll+1)%n;
rr=(rr-1+n)%n;
}
ans+=ff;
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--) {
scanf("%d",&n);
for (int i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
ans=0;
edge_cnt();
if (n%2==0) point_cnt();
printf("%d\n",ans);
}
return 0;
}
方法二
把每条边和每个角转换为数值,求环形序列的回文串的个数
转换方法:边→长度的平方,角→向量积的模
环形序列回文串:加倍线形序列
回文串问题字符串:Manacher
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
const int N=1000005;
struct node{
int x,y;
};
node p[N];
int n,cnt,RL[N<<3];
int a[N<<2],s[N<<3];
int Cross(int a,int b,int c) {
node o,_;
o.x=p[b].x-p[a].x; o.y=p[b].y-p[a].y;
_.x=p[c].x-p[a].x; _.y=p[c].y-p[a].y;
return o.x*_.y-o.y*_.x;
}
int dis(int i,int j) {
return (p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y);
}
void manacher() {
s[0]=-2;
int len;
for (int i=1;i<=cnt;i++) {
s[i*2-1]=-1;
s[i*2]=a[i];
}
s[cnt*2+1]=-1;
s[cnt*2+2]=-3;
len=cnt*2+1;
int ans=0;
int mx=0,pos=0;
for (int i=1;i<=len;i++) {
int j=i*2-pos;
if (i<=pos)
RL[i]=min(mx-i,RL[j]);
else RL[i]=1;
while (s[i+RL[i]]==s[i-RL[i]]) RL[i]++;
if (i+RL[i]>mx)
mx=i+RL[i],pos=i;
if (RL[i]-1>=n*2) ans++;
}
printf("%d\n",ans/2);
}
int main()
{
int T;
scanf("%d",&T);
while (T--) {
scanf("%d",&n);
for (int i=0;i<n;i++)
scanf("%d%d",&p[i].x,&p[i].y);
cnt=0;
for (int i=0;i<n;i++) {
a[++cnt]=Cross(i,(i-1+n)%n,(i+1)%n);
a[++cnt]=dis(i,(i+1)%n);
}
int tmp=cnt;
for (int i=1;i<=tmp;i++) //环
a[++cnt]=a[i];
manacher();
}
return 0;
}