翔哥的hu测 T2. 数学作业(计算几何||hash+manacher)

61 篇文章 0 订阅
42 篇文章 3 订阅

版权属于ZYXZYXZYX,想要引用此题(包括题面)的朋友请联系博主

这里写图片描述
这里写图片描述

题目来源:[POI 2007] Axes of Symmetry

原题提交地址

分析:
可能是数据较弱
所以用计算几何A掉了
(然而翔哥把我的程序放到原题网站上,一样A掉了。。。果然我是打暴力的料)

方法一

对于多边形的对称轴,我们要分类讨论:

  • n为偶数
    • 点与点之间的连线形成的对称轴:
      我们枚举多边形上的点 A A ,因为对称轴两边的点数量一定相等,所以我们可以直接找到A在多边形上的对称点 B B
      得到line(A,B),暴力判断是否点对都关于 line(A,B) l i n e ( A , B ) 对称
    • 边的中垂线形成的对称轴:
      我们枚举多边形上的一条边 AB A B ,得到 AB A B 的中垂线 l l ,暴力判断是否点对都关于l对称
  • n为奇数
    • 点与点之间的连线形成的对称轴:
      因为对称轴两边的点数量一定相等,所以这样的对称轴不会存在
    • 边的中垂线形成的对称轴:
      我们枚举多边形上的一条边 AB A B ,得到 AB A B 的中垂线 l 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.以下sql,使用了subplan+broadcast, 请根据语义合理优化该sql, 使其运行效率更高效 select * from user01.tb1 t1 where exists (select max(id) from user01.tb2 t2 where t1.name=t2.name); 2.以下SQL, t1表使用了broadcast算子,请使用 hint 优化,避免t1表使用 broadcast select t1,id,t2.id2 from user01.tb1 t1 inner join user01.t_skew t2 on t1.id=t2.id2 and t1.name='beijing' order by 1; 3.如何判断下列语句是否下推,请写出判断方法: select count(t1.*) from user01.tb1 t1 left join user01.tb2 t2 on t1.id=t2.td and t2.name ='beijing' 4.下列语句的执行计划中,优化器选择表他作为hash内表,t2作为hash外表,请使用hint调整执行计划,使t2hash内表 select t1.id,t2.id2 from user01.tb1 t1 inner join user01.t_skew t2 on t1.id=t2.id2 and t1.name ='beijing' order by 1 5.将schema权限赋予用户user1 将schema s2下所有表的访问权限赋予用户user1 6.gsql开创建数据库usdb,指定字符集utf-8,限制连接数20启时间检查命令 7.创建名为us01的用户,并将sysadmin权限授权给他 8.创建数据库usdb,指定字符集utf-8,限制连接数20 9.下面的语句的执行计划中州优化器选择了nestloop的关联方式,请根据语义修改语句,其实关联方式变为hashion,以提升查询性能 select * from user01.tb1 t1 where t1.id not in(select t2.id from user01.tb2 t2 where t2.name='shanghai'); 改写参考: select * from user01.tb1 t1 where not exists (select 1 from user01.tb2 t2 where t2.name='shanghai' and t1.id=t2.id) and t1.id is not null; 10.1、下面的执行计划存在性能问题,已知该集群有10个dn,且 select reltuples from pg class Where relname='t1'; 结果为123456798765,请根据执行计划分析,判断性能瓶颈,并给出优化建议: id | operation | E-rows | E-memory | E-width | E-costs ----+-------------------------------+-----------+---------------+-----------|---------- 1 |->Streaming (type: GATHER) | 100 | | 12 | 114.24 2 | ->Hash Join(3,4) | 100 | 1MB | 12 | 111.05 3 | ->Seq Scan on t2 | 100000 | 1MB | 4 | 91.50 4 | ->Hash | 96 | 16MB | 8 | 3.56 5 | ->Streaming(type: BROADCAST) |100| 2MB | 8 | 3.56 6 | ->Seg Scan on t1 | 100 | 1MB | 8 | 3.06
最新发布
07-24

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值