[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;
}
posted @ 2019-02-13 16:36  Hyscere  阅读(48)  评论(0编辑  收藏