CF213E Two Permutations

考虑到都是排列,值域连续,于是 \(a\) 都加 \(x\) 之后相当于在值域上平移了一段,也是连续的。由于要进行比较,个很容易想到哈希。\(a\) 的哈希值很好维护,每次平移一位加上 \(\sum BASE^i\) 即可。考虑如何快速取出 \(b\) 中在这段值域内的数的哈希值。

不妨设 \(p[i]\) 表示数 \(i\)\(b\) 中出现的位置.值域类似一个滑动窗口,当 \(i\) 进入的时候就在 \(p[i]\) 插入 \(i\),删除同理,用线段树可以简单维护处哈希值。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 2e5 + 5, BSE = 23;

unsigned long long tree[N << 2], pw[N];
int a[N], b[N], p[N], n, m, sum[N << 2];

inline int read() {
	register int s = 0; register char ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
	return s;
}

inline void update(int now) {
	sum[now] = sum[now << 1] + sum[now << 1 | 1];
	tree[now] = (1ll * tree[now << 1] * pw[sum[now << 1 | 1]]);
	tree[now] += tree[now << 1 | 1];
}

inline void insert(int now, int l, int r, int q, int k) {
	if (l >= r) {
		if (k < 0) tree[now] = 0, --sum[now];
		else tree[now] = k, ++sum[now];
		return ;
	} int mid = l + r >> 1;
	if (q <= mid) insert(now << 1, l, mid, q, k);
	else insert(now << 1 | 1, mid + 1, r, q, k);
	update(now);
}

int main() {
	n = read(); m = read();
	for (int i = 1; i <= n; ++i) a[i] = read();
	pw[0] = 1; for (int i = 1; i <= m; ++i) p[b[i] = read()] = i, pw[i] = (23ll * pw[i - 1]);
	unsigned long long hsh = 0, del = 0;
	for (int i = 1; i <= n; ++i) hsh = (23ll * hsh + a[i]), del = (del + pw[i - 1]);
	int ans = 0;
	for (int i = 1; i <= m; ++i) {
		if (i > n) insert(1, 1, m, p[i - n], -1);
		insert(1, 1, m, p[i], i);
		if (i >= n) { 
			if (tree[1] == hsh) ++ans;
			hsh = hsh + del;
		} 
	} printf("%d", ans);
	return 0;
}
posted @ 2021-10-15 21:20  Smallbasic  阅读(42)  评论(0)    收藏  举报