CF213E Two Permutations 题解
题目要求的是新的 $a$ 序列是 $b$ 序列的子序列的个数!而不是子串(或者连续子序列)。我因为读错题调 KMP 调了一个小时。
如果要求的是子串那做法就大不相同了。
进入正题,首先思考没有 $+ x$ 的操作时怎么做。
因为 $a$ 序列和 $b$ 序列都是排列,所以出现的元素一定是 $1 \sim n$ 和 $1 \sim m$ 并且每种元素正好出现一次。
利用这一点我们可以把 $b$ 序列中没有在 $a$ 序列中出现过的元素排除掉。
具体地,我们需要只把 $b$ 序列中属于 $[1, n]$ 的元素按照原来的相对顺序保存下来即可。
做完这一步后,我们已经把一个可能符合要求的子序列提取出来了。
因为若出现了别的元素那么一定不会符合题目要求。
接下来需要我们判断这个子序列是否和 $a$ 相同,我们可以 $\mathcal{O}(n)$ 地扫一遍,但是在 $n$ 很大的时候效率会非常低。
尝试改成哈希,我们就将判断的时间复杂度从 $\mathcal{O}(n)$ 降到了 $\mathcal{O}(1)$,但是相应地,预处理的时间复杂度就从 $\mathcal{O}(1)$增长到了 $\mathcal{O}(n)$。(这里指的是比较两序列是否相同的时间复杂度,不包括前文提到的提取子序列的复杂度。)
乍一看改成哈希没有什么优化,但是这仅仅是不考虑 $+ x$ 操作的情况。
这时候把题目给的 $+ x$ 操作加上来。
如果对原序列所有元素都 $+ x$,对序列哈希值的贡献就是:
$$ x\times\sum\limits_{i = 1}^{n}base^{i - 1} $$
这里的 $\sum\limits_{i = 1}^{n}base^{i - 1}$ 可以提前计算,更改时只需要乘上 $x$ 就好。
这样,我们就做到了 $\mathcal{O}(n)$ 预处理,$\mathcal{O}(1)$ 更改哈希值和 $\mathcal{O}(1)$ 比较了。
然后就是考虑如何快速提取 $b$ 中的子序列。
注意到前文所述的“把 $b$ 序列中属于 $[1, n]$ 的元素按照原来的相对顺序保存下来”,假设 $x = 1$,那么我们要保存的元素区间就是 $[2, n + 1]$ 了。
因为 $b$ 序列是排列,所以范围区间 $[1, n]$ 到 $[2, n + 1]$ 只会引起删除掉一个元素和加入一个新的元素的变化。
又因为 $b$ 序列是静态的,所以属于 $[2, n]$ 的元素的相对顺序不会变化,我们们只需要先删除掉 $1$ 元素,再找到 $n + 1$ 元素应该处于的位置插入即可,删除和插入的时候同时维护一下序列哈希值。
类似的,我们可以从 $[1, n]$ 变为 $[2, n + 1]$ 推广到 $[l, r]$ 变为 $[l + 1, r + 1]$,具体步骤不再阐述。
我们需要一个动态维护序列的,可以快速删除指定位置元素,快速在指定位置插入元素的数据结构,平衡树可以满足这些要求。
对于处理出一个元素应该在子序列中的哪个位置,只需要知道在它前面有多少个元素即可,这一点可以用树状数组实现,为了方便操作以及优化时间复杂度,可以提前按照元素从小到大的顺序为 $b$ 序列排序,同时要保存元素在原 $b$ 序列的位置。
给出代码实现(双哈希+平衡树+树状数组):
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod1 = 998244353, mod2 = 1000000007, mul1 = 299983, mul2 = 19260817;
int n, m, ans, l, mid, r, pos, a[200005], b[200005], c[200005], rnk[200005];
ll state1, state2, add1, add2, power1[200005], power2[200005];
struct FHQ_Treap {
struct node {
ll v1, v2, len;
node(ll V1 = 0, ll V2 = 0, ll Len = 0): v1(V1), v2(V2), len(Len) {}
node operator = (const node& _) {
v1 = _.v1, v2 = _.v2, len = _.len;
return *this;
}
node operator + (const node& _) const {
return node((_.v1 + v1 * power1[_.len] % mod1) % mod1, (_.v2 + v2 * power2[_.len] % mod2) % mod2, len + _.len);
}
} hsh[200005];
int idx, root, lc[200005], rc[200005], val[200005], size[200005], priority[200005];
int new_node(ll v = 0) {
++idx;
hsh[idx] = node(v, v, 1), val[idx] = v, lc[idx] = rc[idx] = 0, size[idx] = 1;
priority[idx] = rand();
return idx;
}
void push_up(int cur) {
size[cur] = size[lc[cur]] + size[rc[cur]] + 1;
hsh[cur] = hsh[lc[cur]] + node(val[cur], val[cur], 1) + hsh[rc[cur]];
}
void split(int cur, int rank, int& l, int& r) {
if(!cur) return (l = r = 0, void());
if(size[lc[cur]] < rank) {
l = cur;
split(rc[cur], rank - size[lc[cur]] - 1, rc[cur], r);
}
else {
r = cur;
split(lc[cur], rank, l, lc[cur]);
}
push_up(cur);
}
int merge(int curl, int curr) {
if(!curl || !curr) return curl | curr;
if(priority[curl] < priority[curr]) {
rc[curl] = merge(rc[curl], curr);
push_up(curl);
return curl;
}
else {
lc[curr] = merge(curl, lc[curr]);
push_up(curr);
return curr;
}
}
} treap;
int lowbit(int x) {return x & -x;}
void change(int x, int v) {
while(x <= 200000) c[x] += v, x += lowbit(x);
}
int ask(int x) {
int ret = 0;
while(x) ret += c[x], x -= lowbit(x);
return ret;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
srand(time(nullptr));
cin >> n >> m;
power1[0] = power2[0] = 1;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
state1 = (state1 * mul1 + a[i]) % mod1;
state2 = (state2 * mul2 + a[i]) % mod2;
power1[i] = power1[i - 1] * mul1 % mod1;
power2[i] = power2[i - 1] * mul2 % mod2;
add1 = (add1 + power1[i - 1]) % mod1;
add2 = (add2 + power2[i - 1]) % mod2;
}
for(int i = 1; i <= m; ++i) {
cin >> b[i];
rnk[i] = i;
}
stable_sort(rnk + 1, rnk + 1 + m, [&](int p1, int p2) -> bool {return b[p1] < b[p2];});
for(int i = 1; i <= m; ++i) {
if(i > n) {
pos = ask(rnk[i - n]);
change(rnk[i - n], -1);
treap.split(treap.root, pos, l, r);
treap.split(l, pos - 1, l, mid);
treap.root = treap.merge(l, r);
}
pos = ask(rnk[i]);
change(rnk[i], 1);
treap.split(treap.root, pos, l, r);
treap.root = treap.merge(treap.merge(l, treap.new_node(i)), r);
if(i >= n) {
if(treap.hsh[treap.root].v1 == state1 && treap.hsh[treap.root].v2 == state2) ++ans;
state1 = (state1 + add1) % mod1;
state2 = (state2 + add2) % mod2;
}
}
cout << ans;
return 0;
}
本文来自博客园,作者:A_box_of_yogurt,转载请注明原文链接:https://www.cnblogs.com/A-box-of-yogurt/p/18016418

浙公网安备 33010602011771号