CF213E Two Permutations 题解

· · 题解

CF213E Two Permutations 题解

题目大意

给出两个排列 a, b,长度分别为 n, m,你需要计算有多少个 x,使得 a_1 + x, a_2 + x, \dots, a_n + xb 的子序列

n \leq m \leq 2 \times 10^5

分析

挺不错的一道题。

判断两个排列是否相同,不难想到哈希。动态维护哈希值,不难想到线段树。

题解

其实分析里已经把思路说的差不多了,就是动态维护哈希值并判断是否相等。

可以发现,x 一定不会很大,具体来说,x \in [0, m - n]

所以我们可以枚举每一个 x,判断在排列 b 中,值域在 [x, n + x] 里的子序列构成的哈希值与 a 排列的哈希值是否相等。

主要来说说每次让 x 加一时两个排列的哈希值怎么变。

设一个排列 p 的哈希值 Hash_p = base^{n - 1} \times p_1 + base^{n - 2} \times p_2 + \dots + base^0 \times p_n

每次 x 加一时,Hash_a 会加上 base^{n - 1} + base^{n - 2} + \dots + base^0,这个不难发现。而这个值我们可以预处理出来,每次直接加就行。

所以这就是个线段树,支持区间乘,区间个数查询(就是上述的 $cnt$),区间和查询。 答案就很容易得出来了,$ans = \sum_{x = 0}^{m - n} [Hash_a = Hash_b]$。 ## 代码 ```cpp #include <bits/stdc++.h> #define int long long using namespace std; const int M = 200005; const int mod = 1e9 + 21; const int base = 13331; int n, m, a[M], stand, b[M], loc[M], ans, has[M], inv, tmp; int sum[M << 2], lazy[M << 2], cnt[M << 2]; inline int quick_pow(int base, int ci, int pp) { int res = 1; while(ci) { if(ci & 1) res = res * base % pp; base = base * base % pp; ci >>= 1; } return res; } inline void push_down(int rt) { if(lazy[rt] != 1) { int ls = rt << 1, rs = ls | 1; sum[ls] = sum[ls] * lazy[rt] % mod; sum[rs] = sum[rs] * lazy[rt] % mod; lazy[ls] = lazy[rt] * lazy[ls] % mod; lazy[rs] = lazy[rt] * lazy[rs] % mod; lazy[rt] = 1; } } void build(int rt, int l, int r) { sum[rt] = 0; lazy[rt] = 1; if(l == r) return; int mid = (l + r) >> 1, ls = rt << 1, rs = ls | 1; build(ls, l, mid); build(rs, mid + 1, r); } int ask(int rt, int l, int r, int zuo, int you) { if(zuo <= l && r <= you) return cnt[rt]; push_down(rt); int mid = (l + r) >> 1, ls = rt << 1, rs = ls | 1, res = 0; if(zuo <= mid) res = ask(ls, l, mid, zuo, you); if(you > mid) res = (res + ask(rs, mid + 1, r, zuo, you)) % mod; sum[rt] = (sum[ls] + sum[rs]) % mod; return res; } void add(int rt, int l, int r, int zuo, int you, int addend) { if(zuo <= l && r <= you) { lazy[rt] = lazy[rt] * addend % mod; sum[rt] = sum[rt] * addend % mod; return; } push_down(rt); int mid = (l + r) >> 1, ls = rt << 1, rs = ls | 1; if(zuo <= mid) add(ls, l, mid, zuo, you, addend); if(you > mid) add(rs, mid + 1, r, zuo, you, addend); sum[rt] = (sum[ls] + sum[rs]) % mod; } void change(int rt, int l, int r, int pos, int addend) { cnt[rt] += addend > 0 ? 1 : -1; if(l == r) { sum[rt] = addend > 0 ? addend : 0; return; } push_down(rt); int mid = (l + r) >> 1, ls = rt << 1, rs = ls | 1; if(pos <= mid) change(ls, l, mid, pos, addend); else change(rs, mid + 1, r, pos, addend); sum[rt] = (sum[ls] + sum[rs]) % mod; } signed main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin >> n >> m; inv = quick_pow(base, mod - 2, mod); has[0] = 1; for(int i = 1; i <= n; ++ i) { cin >> a[i]; stand = (stand * base + a[i]) % mod; has[i] = has[i - 1] * base % mod; tmp += has[i - 1]; } for(int i = 1; i <= m; ++ i) cin >> b[i], loc[b[i]] = i; int cur = 0; build(1, 1, m); for(int i = 1; i <= m; ++ i) if(b[i] <= n) ++ cur, change(1, 1, m, i, b[i] * has[n - cur] % mod); if(sum[1] == stand) ++ ans; for(int i = n + 1; i <= m; ++ i) { add(1, 1, m, 1, loc[i - n], inv); change(1, 1, m, loc[i - n], -1); add(1, 1, m, 1, loc[i], base); change(1, 1, m, loc[i], i * has[ask(1, 1, m, loc[i], m)] % mod); stand = (stand + tmp) % mod; if(sum[1] == stand) ++ ans; } cout << ans; } ```