码迷,mamicode.com
首页 > 其他好文 > 详细

NowCoder 20475 - 无序运动MOVEMENT

时间:2019-04-19 20:14:47      阅读:199      评论:0      收藏:0      [点我收藏+]

标签:first   出现   space   gcd   mat   次数   div2   题解   namespace   

题目链接

题目描述

??每一个点都是一个二元组 \((x,y)\),定义一个轨迹为一个点列 \(P=(p_1, p_2,\dots,p_n)\) 。轨迹 \(Q = (q_1, q_2,\dots,q_m)\)\(P\) 中的一次出现,当且仅当 \(Q\) 中每一个点经过有限次的平移、旋转、翻转、放缩之后得到 \(Q’\)\(P\) 的一个子序列。

??现在给定一个主轨迹和一些子轨迹,求出每个子轨迹在主轨迹中出现的次数。

题解

??轨迹的相似可以转变为相邻两点的差向量的相似,而在平移、旋转、放缩之后,只要两个轨迹相邻两个差向量所成的有向角的大小,以及向量模比值都相同,这两个轨迹就是相似的。因此,我们可以以向量为节点,有向角和模比值为边构造 AC 自动机,再进行匹配即可。

&emsp,?对于翻转的情况,我们发现翻转两次是没有意义的。因此,如果一个轨迹和它翻转之后的轨迹不相同,就把它翻转之后也加入 AC 自动机。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
const int maxn=4e5+5;
struct cplx{
    int x,y;
    cplx(int xx=0,int yy=0){
        x=xx; y=yy;
    }
};
cplx operator + (cplx a,cplx b){
    return cplx(a.x+b.x,a.y+b.y);
}
cplx operator - (cplx a,cplx b){
    return cplx(a.x-b.x,a.y-b.y);
}
int dot(cplx a,cplx b){
    return a.x*b.x+a.y*b.y;
}
int det(cplx a,cplx b){
    return a.x*b.y-a.y*b.x;
}
int gcd(int a,int b){
    while (b){
        swap(a,b); b%=a;
    }
    return a<0?-a:a;
}
struct node{
    int x2,y2,dot,det;
};
node cal(cplx a,cplx b){
    node nd;
    int t;
    nd.x2=dot(a,a); nd.y2=dot(b,b);
    t=gcd(nd.x2,nd.y2); nd.x2/=t; nd.y2/=t;
    nd.dot=dot(a,b); nd.det=det(a,b);
    t=gcd(nd.dot,nd.det); nd.dot/=t; nd.det/=t;
    return nd;
}
bool operator < (node a,node b){
    if (a.x2!=b.x2) return a.x2<b.x2;
    if (a.y2!=b.y2) return a.y2<b.y2;
    if (a.dot!=b.dot) return a.dot<b.dot;
    if (a.det!=b.det) return a.det<b.det;
    return false;
}
int que[maxn];
struct AcAuto{
    map<node,int> ch[maxn];
    int fail[maxn],last[maxn];
    int root,ndcnt,strcnt;
    int pos[maxn];
    bool end[maxn];
    int ans[maxn];
    int nnode(){
        ++ndcnt;
        fail[ndcnt]=0;
        last[ndcnt]=0;
        end[ndcnt]=false;
        ans[ndcnt]=0;
        ch[ndcnt].clear();
        return ndcnt;
    }
    AcAuto(){
        ndcnt=0;
        strcnt=0;
        root=nnode();
    }
    void clear(){
        ndcnt=0;
        strcnt=0;
        root=nnode();
    }
    void add_str(node a[],int n){
        int u=root,v;
        for (int i=0;i<n;i++){
            if (!ch[u].count(a[i]))
                ch[u][a[i]]=nnode();
            u=ch[u][a[i]];
        }
        pos[strcnt++]=u;
        end[u]=true;
    }
    void build_fail(){
        int u=root,v,w;
        int l,r;
        node nd;
        map<node,int>::iterator it;
        l=r=0;
        que[r++]=u;
        while (l<r){
            u=que[l++];
            for (it=ch[u].begin();it!=ch[u].end();it++){
                v=it->second; nd=it->first;
                que[r++]=v;
                w=fail[u];
                while (w&&!ch[w].count(nd))
                    w=fail[w];
                fail[v]=w?ch[w][nd]:root;
                last[v]=end[fail[v]]?fail[v]:last[fail[v]];
            }
        }
    }
    void addans(int u){
        while (u){
            ans[u]++;
            u=last[u];
        }
    }
    void query(node a[],int n){
        int u=root,v;
        addans(u);
        for (int i=0;i<n;i++){
            while (u&&!ch[u].count(a[i])) u=fail[u];
            u=u?ch[u][a[i]]:root;
            addans(u);
        }
    }
} ac_auto;
cplx arr[maxn];
node nd[maxn];
bool div2[maxn];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    ac_auto.clear();
    for (int i=0;i<m;i++){
        int k;
        scanf("%d",&k);
        for (int j=0;j<k;j++){
            scanf("%d%d",&arr[j].x,&arr[j].y);
            if (j) arr[j-1]=arr[j]-arr[j-1];
        }
        k-=2;
        for (int j=0;j<k;j++)
            nd[j]=cal(arr[j],arr[j+1]);
        ac_auto.add_str(nd,k);
        for (int j=0;j<k;j++)
            nd[j].det=-nd[j].det;
        ac_auto.add_str(nd,k);
        div2[i]=true;
        for (int j=0;j<k;j++)
            if (nd[j].det!=0) div2[i]=false;
    }
    for (int i=0;i<n;i++){
        scanf("%d%d",&arr[i].x,&arr[i].y);
        if (i) arr[i-1]=arr[i]-arr[i-1];
    }
    n-=2;
    for (int i=0;i<n;i++)
        nd[i]=cal(arr[i],arr[i+1]);
    ac_auto.build_fail();
    ac_auto.query(nd,n);
    for (int i=0;i<m;i++)
        printf("%d\n",(ac_auto.ans[ac_auto.pos[i*2]]+ac_auto.ans[ac_auto.pos[i*2+1]])/(div2[i]?2:1));
    return 0;
}

NowCoder 20475 - 无序运动MOVEMENT

标签:first   出现   space   gcd   mat   次数   div2   题解   namespace   

原文地址:https://www.cnblogs.com/Kilo-5723/p/10738182.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!