BZOJ 1039. [ZJOI2008]无序运动Movement
平移、旋转、放缩对两个相似三角形没有影响,那么一个长度为 $n$ 的轨迹就可以描述为 $n-2$ 个三角形,每个三角形就用相邻两边长来描述,还得加上第二条线段在第一条线段的逆时针还是顺时针方向,因为如果不加这个就会出现翻不翻转带来的影响,然后就变成了字符串匹配了。不过由于字符集很大,得用 map 来存边。然后翻转一下再做一遍。
如果一个轨迹共线的话,翻转后他会被重新算一遍,所以要除以 $2$。
如果一个轨迹长度小于 $3$ 的话, 他就能匹配上所有长度相等的子串。
#include <bits/stdc++.h> namespace IO { char buf[1 << 21], buf2[1 << 21], a[20], *p1 = buf, *p2 = buf, hh = '\n'; int p, p3 = -1; void read() {} void print() {} inline int getc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++; } inline void flush() { fwrite(buf2, 1, p3 + 1, stdout), p3 = -1; } template <typename T, typename... T2> inline void read(T &x, T2 &... oth) { T f = 1; x = 0; char ch = getc(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getc(); } while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getc(); } x *= f; read(oth...); } template <typename T, typename... T2> inline void print(T x, T2... oth) { if (p3 > 1 << 20) flush(); if (x < 0) buf2[++p3] = 45, x = -x; do { a[++p] = x % 10 + 48; } while (x /= 10); do { buf2[++p3] = a[p]; } while (--p); buf2[++p3] = hh; print(oth...); } } struct P { int x, y; void read() { IO::read(x, y); } void print() { printf("%d %d\n", x, y); } P() {} P(int x, int y): x(x), y(y) {} P operator + (const P &p) const { return P(x + p.x, y + p.y); } P operator - (const P &p) const { return P(x - p.x, y - p.y); } int det(const P &p) const { return x * p.y - y * p.x; } int abs2() { return x * x + y * y; } }; struct Node { int a, b, c, dir; bool operator < (const Node &p) const { if (a != p.a) return a < p.a; if (b != p.b) return b < p.b; if (c != p.c) return c < p.c; return dir < p.dir; } bool operator == (const Node &p) const { return !(*this < p) && !(p < *this); } }; const int N = 2e5 + 7; int n, m, par[N], tol, ans[N]; std::vector<P> point[N], all; std::vector<int> flag[N]; std::map<Node, int> mp[N]; int gcd(int a, int b) { while (b) { a %= b; std::swap(a, b); } return a; } Node getnode(const P &A, const P &B, const P &C) { int lena = (B - A).abs2(), lenb = (B - C).abs2(), lenc = (A - C).abs2(); int g = gcd(lena, gcd(lenb, lenc)); lena /= g, lenb /= g, lenc /= g; int crs = 0; if ((B - A).det(C - B) > 0) crs = 1; else if ((B - A).det(C - B) < 0) crs = -1; return {lena, lenb, lenc, crs}; } void done(std::vector<P> vec, int id) { if (vec.size() < 3) { ans[id] = n - vec.size() + 1; return; } par[id] = 1; int rt = 0; for (int i = 0; i < vec.size() - 2; i++) { Node o = getnode(vec[i], vec[i + 1], vec[i + 2]); if (o.dir) par[id] = 0; auto it = mp[rt].find(o); if (it == mp[rt].end()) { mp[rt][o] = ++tol; rt = tol; } else { rt = it->second; } } flag[rt].push_back(id); } int fail[N], last[N], cnt[N]; void build() { std::queue<int> que; for (auto it: mp[0]) que.push(it.second); while (!que.empty()) { int u = que.front(); que.pop(); for (auto it: mp[u]) { Node cur_node = it.first; int f = fail[u], v = it.second; for (; f && mp[f].find(cur_node) == mp[f].end(); f = fail[f]); if (mp[f].find(cur_node) != mp[f].end()) f = mp[f][cur_node]; fail[v] = f; last[v] = flag[fail[v]].empty() ? last[fail[v]] : fail[v]; que.push(v); } } } void solve(const std::vector<P> &vec) { int rt = 0; for (int i = 0; i < n - 2; i++) { Node node = getnode(vec[i], vec[i + 1], vec[i + 2]); for (; rt && mp[rt].find(node) == mp[rt].end(); rt = fail[rt]); if (mp[rt].find(node) != mp[rt].end()) rt = mp[rt][node]; for (int j = rt; j; j = last[j]) ++cnt[j]; } } int main() { IO::read(n, m); for (int i = 1; i <= m; i++) { int k; IO::read(k); point[i].resize(k); for (int j = 0; j < k; j++) point[i][j].read(); done(point[i], i); } build(); all.resize(n); for (int i = 0; i < n; i++) all[i].read(); solve(all); for (int i = 0; i < n; i++) all[i].y *= -1; solve(all); for (int i = 1; i <= tol; i++) for (int u: flag[i]) ans[u] += cnt[i] / (par[u] + 1); for (int i = 1; i <= m; i++) IO::print(ans[i]); IO::flush(); return 0; }