【Manacher】【KMP】【计算几何】lydsy1100 对称轴osi

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值