NowCoder 20475 - 无序运动MOVEMENT

题目链接

题目描述

??每一个点都是一个二元组 \((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;
}

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

时间: 2024-08-30 17:27:26

NowCoder 20475 - 无序运动MOVEMENT的相关文章

[bzoj1039] [ZJOI2008]无序运动Movement

Description D博士对物理有着深入的研究,经典物理.天体物理.量子物理都有着以他的名字命名的定理.最近D博士着迷于研究粒子运动的无规则性.对圣经深信不疑的他相信,上帝创造的任何事物必然是有序的.有理可循的,而不是无规则的.混沌的. 经过长时间的研究,D博士找到了很多出现相当频繁的轨迹片断,他把这些轨迹片断储存在一个很大的数据库内.他需要你帮助他写一个程序,对于一个给出的粒子运动轨迹,统计数据库中每个轨迹片断的出现的次数. 为清楚起见,我们定义一个粒子的轨迹为二维平面上的一个点列(P1,

[nowCoder] 两个不等长数组求第K大数

给定两个有序数组arr1和arr2,在给定一个整数k,返回两个数组的所有数中第K小的数.例如:arr1 = {1,2,3,4,5};arr2 = {3,4,5};K = 1;因为1为所有数中最小的,所以返回1: arr1 = {1,2,3};arr2 = {3,4,5,6};K = 4;因为3为所有数中第4小的数,所以返回3: 要求:如果arr1的长度为N,arr2的长度为M,时间复杂度请达到O(log(min{M,N})). 这题目的难度在于时间复杂度请达到O(log(min{M,N})),参

[nowCoder] 局部最小值位置

定义局部最小的概念.arr长度为1时,arr[0]是局部最小.arr的长度为N(N>1)时,如果arr[0]<arr[1],那么arr[0]是局部最小:如果arr[N-1]<arr[N-2],那么arr[N-1]是局部最小:如果0<i<N-1,既有arr[i]<arr[i-1]又有arr[i]<arr[i+1],那么arr[i]是局部最小.给定无序数组arr,已知arr中任意两个相邻的数都不相等,写一个函数,只需返回arr中任意一个局部最小出现的位置即可. 分析:

[nowCoder] 子数组最大乘积

给定一个double类型的数组arr,其中的元素可正可负可0,返回子数组累乘的最大乘积.例如arr=[-2.5,4,0,3,0.5,8,-1],子数组[3,0.5,8]累乘可以获得最大的乘积12,所以返回12. 分析,是一个dp的题目, 设f[i]表示以i为结尾的最大值,g[i]表示以i结尾的最小值,那么 f[i+1] = max{f[i]*arr[i+1], g[i]*arr[i+1],arr[i+1]} ,只有这三种情况. 考虑到f[i],g[i]只和i-1有关,那么可以用局部变量即可搞定,

Irrlicht 3D Engine 笔记系列之 教程4 - Movement

作者: i_dovelemon 日期: 2014 / 12 / 16 来源: CSDN 主题: Event Receiver, Animator, Framerate independent movement and framerate dependent movement 引言 从今天開始,博主将进行对3D Engine的学习.而且,在博客中将自己学习的心得一一分享给大家.希望可以对大家有所帮助.也希望可以找到志同道合的同伴一起学习3D 游戏引擎方面的知识. 为什么选择Irrlicht? 在非

NowCoder猜想(素数筛法+位压缩)

在期末被各科的大作业碾压快要窒息之际,百忙之中抽空上牛客网逛了逛,无意中发现一道好题,NowCoder猜想,题意很明显,就是个简单的素数筛法,但竟然超内存了,我晕(+﹏+)~  明明有 3 万多 k 的空间限制……于是我不打表,试了试最暴力的做法,赤裸裸的做法果然超时了,无奈,只好对素数筛法进行位压缩了,这是我目前所能想到的方法了,第一次用上这样的特技,还是调了好一会(位数组里不能用 bool 来定义,具体的话好像 bool 和 int 之类的整型稍有不同:也不能用 int,因其最高位是正负标志

alter table table_name enable row movement

Row movement 从字面意思解释为行移动.默认情况下,oracle数据块中的一行其生命周期内是不会发生移动的,即其rowid不会发生改变. 但是在某些情景下,我们希望行的rowid可以发生变化,这时候我们就需要启动表的row movement特性. 启用row movement特性,使用如下语句: Alter table table_name enable row movement; 通查在三种情景下,需要启用row movement. 1:分区表 当我们允许分区表的分区键是可更新的时候

[NowCoder] 藏宝图

---恢复内容开始--- 牛牛拿到了一个藏宝图,顺着藏宝图的指示,牛牛发现了一个藏宝盒,藏宝盒上有一个机关,机关每次会显示两个字符串 s 和 t,根据古老的传说,牛牛需要每次都回答 t 是否是 s 的子序列.注意,子序列不要求在原字符串中是连续的,例如串 abc,它的子序列就有 {空串, a, b, c, ab, ac, bc, abc} 8 种. 输入描述: 每个输入包含一个测试用例.每个测试用例包含两行长度不超过 10 的不包含空格的可见 ASCII 字符串. 输出描述: 输出一行 "Yes

A novel method for identifying behavioural changes in animal movement data

A novel method for identifying behavioural changes in animal movement data A novel method for identifying behavioural changes in animal movement data 词语翻译 相关定义 正文 Abstract Introduction Methods Application to data Northern fur seal Discussion 词语翻译 mov