ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[ZJOI2008] 无序运动 题解

2022-07-20 09:01:22  阅读:136  来源: 互联网

标签:typedef return int 题解 无序 cin Vec ZJOI2008 define


你谷 link

一道蛮模版的字符串题,只需要一步非常显然的转化。

首先思考如果无视四种操作该怎么做,如果将每一个点看成是一个字符,那么就是给定一个文本串,多个模式串,求每个模式串在文本串里出现几次,这显然是一个字符串的模板题,可以使用 AC 自动机,也可以使用诸如后缀数组、后缀树、后缀自动机之类的后缀数据结构维护,这里不多做赘述,可以转到你谷模板题自行学习。

那么就是转化四种操作,考虑平移和旋转就是无视了绝对位置,缩放就是不关心绝对距离,翻转我们最后处理。

既然不关心绝对距离和绝对位置,我们就可以考虑现将相邻两点连线,然后存向量,转化为向量,发现再绝对的数值失去作用后,我们只能将相邻两向量的关系存下来,显然我们需要存长度的比值和夹角,如果用 double 存精度肯定没有保证,我们可以选择使用整数存下来,长度的比值我们可以直接以分数形式记录,夹角我们知道任意一个角都可以用一组正弦和余弦值唯一确定,而再向量中,我们知道点积和叉积是与正余弦相关的,但是我们只关心夹角,同理我们将正弦和余弦约分,即同时除以它们的最大公因数,正确性证明显然。

那么我们对于相邻的三个点求出两个向量,再对两个向量求出长度的比值和点叉积的比值,以四元组形式存下来作为“字符”,可以用 c++ 内置的 tuple 存储,各种自动机的转移边可以利用 map 来实现。

这里我选择用符合思维惯性的后缀自动机实现,在翻转方面,直接将给定的模式串原串翻转后重新求四元组序列,如果和原串的四元组序列相同(这种情况即给定的所有点共线),则不需要再管翻转,否则再跑一边,将两次的答案加起来,显然不会有重,注意特判只给了两个点的情况。

c++ 代码
#include<bits/stdc++.h>

using namespace std;

#define Reimu inline void // 灵梦赛高
#define Marisa inline int // 魔理沙赛高
#define Sanae inline bool // 早苗赛高

typedef long long LL;
typedef unsigned long long ULL;

typedef pair<int, int> Pii;
typedef tuple<int, int, int> Tiii;
#define fi first
#define se second

template<typename Ty>
Reimu clear(Ty &x) { Ty().swap(x); }

typedef tuple<LL, LL, LL, LL> Data;

const int N = 200010;

int n, m;
vector<tuple<LL, LL, LL, LL>> a[N][2];

struct Vec {
	int x, y;
	friend istream &operator>>(istream &cin, Vec &a) { return cin >> a.x >> a.y; }
	friend Vec operator-(Vec a, Vec b) { return {a.x - b.x, a.y - b.y}; }
	friend LL operator|(Vec a, Vec b) { return 1LL * a.x * b.x + 1LL * a.y * b.y; }
	friend LL operator&(Vec a, Vec b) { return 1LL * a.x * b.y - 1LL * a.y * b.x; }
};
inline Data calc(Vec a, Vec b, Vec c) {
	a = b - a; b = c - b;
	LL A = a | a, B = b | b, C = a | b, D = a & b, t1 = __gcd(A, B), t2 = __gcd(abs(C), abs(D));
	return {A / t1, B / t1, C / t2, D / t2};
}

struct SAM {
	int sz = 1, las = 1;
	int len[N << 1], fa[N << 1], cnt[N << 1], t[N << 1], id[N << 1];
	map<Data, int> ch[N << 1];
	Reimu operator+=(Data o) {
		int p = las; len[las = ++sz] = len[p] + 1; ++cnt[las];
		while (p && !ch[p].count(o)) ch[p][o] = las, p = fa[p];
		if (!p) return void(fa[las] = 1);
		int q = ch[p][o];
		if (len[q] == len[p] + 1) return void(fa[las] = q);
		len[++sz] = len[p] + 1; ch[sz] = ch[q];
		fa[sz] = fa[q]; fa[q] = fa[las] = sz;
		while (p && ch[p][o] == q) ch[p][o] = sz, p = fa[p];
	}
	Reimu operator~() {
		for (int i = 1; i <= sz; ++i) ++t[len[i]];
		for (int i = 1; i <= len[las]; ++i) t[i] += t[i - 1];
		for (int i = 1; i <= sz; ++i) id[t[len[i]]--] = i;
		for (int i = sz; i; --i) cnt[fa[id[i]]] += cnt[id[i]];
	}
	Marisa operator%(vector<Data> &a) {
		int p = 1;
		for (Data o: a) if (!ch[p].count(o)) return 0; else p = ch[p][o];
		return cnt[p];
	}
} sam;

int main() {
	ios::sync_with_stdio(false); cin.tie(nullptr);
	cin >> n >> m;
	for (int i = 1; i <= m; ++i) {
		int c; cin >> c;
		vector<Vec> b(c);
		for (Vec &x: b) cin >> x;
		for (int j = 2; j < c; ++j) a[i][0].emplace_back(calc(b[j - 2], b[j - 1], b[j]));
		for (Vec &x: b) x = {x.x, -x.y};
		for (int j = 2; j < c; ++j) a[i][1].emplace_back(calc(b[j - 2], b[j - 1], b[j]));
	}
	vector<Vec> b(n);
	for (Vec &x: b) cin >> x;
	for (int j = 2; j < n; ++j) sam += calc(b[j - 2], b[j - 1], b[j]);
	~sam;
	for (int i = 1; i <= m; ++i) cout << (a[i][0].empty() ? n - 1 : a[i][0] == a[i][1] ? sam % a[i][0] : sam % a[i][0] + sam % a[i][1]) << '\n';
	return 0;
}

标签:typedef,return,int,题解,无序,cin,Vec,ZJOI2008,define
来源: https://www.cnblogs.com/LaoMang-no-blog/p/16496538.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有