CF213E Two Permutations 题解
Meteor_
·
·
题解
CF213E Two Permutations 题解
题目大意
给出两个排列 a, b,长度分别为 n, m,你需要计算有多少个 x,使得 a_1 + x, a_2 + x, \dots, a_n + x 是 b 的子序列
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;
}
```