[bzoj1039] [ZJOI2008]无序运动Movement

Description

  D博士对物理有着深入的研究,经典物理、天体物理、量子物理都有着以他的名字命名的定理。最近D博士着迷于研究粒子运动的无规则性。对圣经深信不疑的他相信,上帝创造的任何事物必然是有序的、有理可循的,而不是无规则的、混沌的。 经过长时间的研究,D博士找到了很多出现相当频繁的轨迹片断,他把这些轨迹片断储存在一个很大的数据库内。他需要你帮助他写一个程序,对于一个给出的粒子运动轨迹,统计数据库中每个轨迹片断的出现的次数。 为清楚起见,我们定义一个粒子的轨迹为二维平面上的一个点列(P1, P2, … PN)。点列P的一个子列[i, j]定义为P中一段连续的子序列(Pi, Pi+1, … Pj)。点列P的一个子列[u, v]被称为点列Q = (Q1, Q2 … Qv-u+1)在P中的一次出现,当且仅当Q经过有限次的平移、旋转、翻转、放缩之后得到Q’满足Q’k = Pu+k-1(k = 1 … u – v + 1)。 对平面X-Y进行四种操作的解释平移 设平移向量为(dx, dy),则任意点(x,y)平移后的结果为(x+dx, y+dy) 旋转 设旋转角为t,则任意点(x,y)旋转后的结果为 (x cos t – y sin t, x sin t + y cos t) 翻转 任意点(x,y) 翻转后的结果为(x, -y) 放缩 设放缩比例为p (p ≠ 0),则任意点(x,y)放缩后的结果为(px, py)

Input

  第一行两个整数N、M,分别描述待处理的粒子运动轨迹的点列大小与数据库内的轨迹片断个数。接下来M行依次给出每个轨迹片断。每行先是一个正整数K,表示该轨迹片断点列的长度。然后2K个整数,依次描述点列中的K个点的横坐标与纵坐标。接下来一行2N个整数,依次描述待处理的粒子运动轨迹的点列中N个点的横坐标与纵坐标。注:输入中的每条轨迹中任意相邻两点不会相同。

Output

  应包含M行,依次给出每个片段在待处理运动轨迹中的出现次数。

Sample Input

3 2
2 17 0 10 1
3 0 0 1 0 1 -1
0 0 1 0 1 1

Sample Output

2
1

Solution

考虑如果能匹配上,那么两个图形必定相似。

所以一个很简单的想法就是:记录相邻两条边的边长之比和夹角。

但是这样显然由于精度过低,不可行。所以修改一下记录的东西就变成了:

记录两边的边长平方的最简比和带符号的两边向量点积叉积最简比,一共四个整数。

注意这里先不管翻转操作,如果带上翻转操作的话,把匹配串翻一下再做一遍就好了。

然后建\(AC\)自动机,用\(map\)存边,把串丢上去跑就行了。

由于这里字符集过大只能暴力跳\(fail\)指针。

看起来能过就行了(逃。

#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 2e5+10;

#define sqr(x) ((x)*(x))

int n,m,spj[maxn],tot,fail[maxn],cnt[maxn],lst[maxn],ans[maxn],tp[maxn];

struct point {
    int x,y;
    point operator - (const point &rhs) const {return (point){x-rhs.x,y-rhs.y};}
    int operator * (const point &rhs) const {return x*rhs.y-y*rhs.x;}
    int operator ^ (const point &rhs) const {return x*rhs.x+y*rhs.y;}
}s[maxn];

struct node {
    int a,b,c,d;
    bool operator < (const node &rhs) const {
        if(a!=rhs.a) return a<rhs.a;
        if(b!=rhs.b) return b<rhs.b;
        if(c!=rhs.c) return c<rhs.c;
        return d<rhs.d;
    }
}r[maxn];

map<node,int > e[maxn];
vector <int > ed[maxn];

#define iter map<node,int > :: iterator 

void ins(int w,int rs) {
    int now=0;
    for(int i=1;i<=w;i++) {
        iter it=e[now].find(r[i]);
        if(it==e[now].end()) now=(e[now][r[i]]=++tot);
        else now=it -> second;
    }ed[now].push_back(rs);
}

void build() {
    queue<int > q;
    for(iter i=e[0].begin();i!=e[0].end();i++) q.push(i -> second);
    while(!q.empty()) {
        int now=q.front();q.pop();
        for(iter i=e[now].begin();i!=e[now].end();i++) {
            node a=i -> first;int b=i -> second,c=fail[now];
            for(;c&&e[c].find(a)==e[c].end();c=fail[c]);
            if(e[c].find(a)!=e[c].end()) c=e[c][a];fail[b]=c;
            lst[b]=ed[c].empty()?lst[c]:c;q.push(b);
        }
    }
}

node trans(point A,point B,point C) {
    int a=sqr(B.x-A.x)+sqr(B.y-A.y);
    int b=sqr(C.x-B.x)+sqr(C.y-B.y);
    int c=(C-B)*(B-A),d=(C-B)^(B-A);
    int t=__gcd(a,b);a/=t,b/=t;t=__gcd(abs(c),abs(d)),c/=t,d/=t;
    return (node){a,b,c,d};
}

void solve() {
    int now=0;
    for(int i=1;i<=n-2;i++) {
        int x=now;
        for(;x&&e[x].find(r[i])==e[x].end();x=fail[x]);
        if(e[x].find(r[i])!=e[x].end()) x=e[x][r[i]];now=x;
        for(;x;x=lst[x]) cnt[x]++;
    }
}

int main() {
    read(n),read(m);
    for(int i=1;i<=m;i++) {
        int k,flag=1;read(k);
        for(int j=1;j<=k;j++) read(s[j].x),read(s[j].y);
        for(int j=2;j<k;j++) {
            r[j-1]=trans(s[j-1],s[j],s[j+1]);
            if(r[j-1].c) flag=0;
        }
        if(flag) spj[i]=1;
        if(k-2>0) ins(k-2,i),tp[i]=-1;else tp[i]=k;
    }
    build();
    for(int i=1;i<=n;i++) read(s[i].x),read(s[i].y);
    for(int i=2;i<n;i++) r[i-1]=trans(s[i-1],s[i],s[i+1]);
    solve();
    for(int i=1;i<=n;i++) s[i].x=-s[i].x;
    for(int i=2;i<n;i++) r[i-1]=trans(s[i-1],s[i],s[i+1]);
    solve();
    for(int i=1;i<=tot;i++)
        for(int j=0;j<(int)ed[i].size();j++) ans[ed[i][j]]+=cnt[i]/(spj[ed[i][j]]+1);
    for(int i=1;i<=m;i++) write(tp[i]>=0?n-tp[i]+1:ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/hbyer/p/10370593.html

时间: 2024-10-30 09:24:47

[bzoj1039] [ZJOI2008]无序运动Movement的相关文章

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\) 的一个子序列. ??现在给定一个主轨迹和一些子轨迹,求出每个子轨迹在主轨迹中出现的次数. 题解 ??轨迹的相似可以转变为相邻两点的差向量的相似,而在平移

BZOJ 1036: [ZJOI2008]树的统计Count (树链剖分模板题)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14982  Solved: 6081[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

bzoj1037[ZJOI2008]生日聚会

bzoj1037[ZJOI2008]生日聚会 题意: 一排小孩坐着玩游戏.就座的方案满足如下条件:对于任意连续的一段,男孩与女孩的数目之差不超过k.给出男孩数,女孩数和k,求就座方案数除以12345678的余数. 题解: dp方程见程序,i1i2表示当前选了几男几女,i3i4分别表示当前男比女多几个和女比男多几个. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define

BZOJ 1034: [ZJOI2008]泡泡堂BNB( 贪心 )

贪心...用最弱的赢最弱的,用最强的赢最强的,否则用最弱的和最强的比... (贴个官方题解:将双方的选手均按从强到弱排序,然后第一次扫描尽可能用当前剩下的选手中能赢对手当前最强选手中最弱的一个去赢得胜利,若无法做到,则暂时不考虑给对方最强的选手匹配对手.第二遍扫描使用同样策略去获取尽量多的平局.最后剩下的选手任意匹配就能获得最多的分数) -------------------------------------------------------------------- #include<cs

BZOJ 1040: [ZJOI2008]骑士( 树形dp )

这是一个森林中, 每棵树上都有一个环...每棵树单独处理, 找出环上任意一条边断开, 限制一下这条边两端点的情况, 然后就可以树dp了.. ------------------------------------------------------------------------ #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace

BZOJ 题目1036: [ZJOI2008]树的统计Count(Link Cut Tree,修改点权求两个最大值和最大值)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 8421  Solved: 3439 [Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

1036: [ZJOI2008]树的统计Count

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 7496  Solved: 3078[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 II

ZJOI2008泡泡堂BNB

1034: [ZJOI2008]泡泡堂BNB Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1305  Solved: 676[Submit][Status] Description 第XXXX届NOI期间,为了加强各省选手之间的交流,组委会决定组织一场省际电子竞技大赛,每一个省的代表队由n名选手组成,比赛的项目是老少咸宜的网络游戏泡泡堂.每一场比赛前,对阵双方的教练向组委会提交一份参赛选手的名单,决定了选手上场的顺序,一经确定,不得修改.比赛中