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;
}
posted @ 2023-11-04 22:51  A_box_of_yogurt  阅读(13)  评论(0)    收藏  举报  来源
Document