题目链接
https://codeforces.com/contest/213/problem/E
题目大意
给你一个 1 ~ N的排列 A 和一个 1 ~ M 的排列 B ( N <= M )
问有多少个 d 可以使得排列 A 的每个数 + d 后为排列 B 的子序列
解题思路
权值线段树 + hash
只要满足每个 a[i] + d 在 A 中的相对位置和在 B 中的相对位置相同即可
也就是判断 B 中[1~N]、[2~N+1] ... [M - N + 1~ M]与A中[1, N] 的相对位置是否相同
于是我们先可以定义 pos[i] 表示 i 这个数在 B 中的位置(pos[ b[ i ] ] = i)
要判断相对位置是否相同,我们可以通过 hash 来操作
具体是用线段树维护 1 ~ M 的区间, 第i个区间表示 B 数组第i个位置的数是多少
然后简单来说就是再把这些位置的值提取出来计算hash值是否会和A的hash值相同
(细节看代码)
AC_Coder
#include<bits/stdc++.h> #define rep(i,a,n) for (int i=a;i<=n;i++) #define int long long #define ull unsigned long long using namespace std; const int N = 2e5 + 10; const int P = 13331; const int mod = 999998639; ull power[N]; int a[N] , b[N] , pos[N]; struct Seg_ment{ ull pre , tot; int l , r; }tree[N << 2]; void push_up(int id) { tree[id].tot = tree[id << 1].tot + tree[id << 1 | 1].tot; tree[id].pre = (tree[id << 1].pre * power[tree[id << 1 | 1].tot] + tree[id << 1 | 1].pre) % mod; } void build(int l , int r , int id) { tree[id].l = l , tree[id].r = r; tree[id].pre = 0; if(l == r) return ; int mid = l + r >> 1; build(l , mid , id << 1); build(mid + 1 , r , id << 1 | 1); push_up(id); } void update(int id , int pos , int val) { if(tree[id].l == tree[id].r) { if(!val) tree[id].tot -= 1; else tree[id].tot += 1; tree[id].pre = val; return ; } int mid = tree[id].l + tree[id].r >> 1; if(pos <= mid) update(id << 1 , pos , val); if(pos > mid) update(id << 1 | 1 , pos , val); push_up(id); } void init() { power[0] = 1; rep(i , 1 , 200000) power[i] = power[i - 1] * P % mod; } signed main() { init(); int n , m , ans = 0; cin >> n >> m; build(1 , m , 1); ull sum = 0 , add = 0; rep(i , 1 , n) cin >> a[i] , sum = (sum * P + a[i]) % mod , add += power[i - 1] , add %= mod; rep(i , 1 , m) cin >> b[i] , pos[b[i]] = i; rep(i , 1 , m) { if(i > n) update(1 , pos[i - n] , 0); update(1 , pos[i] , i); int cha = i - n; if(cha >= 0 && tree[1].pre % mod == (sum + cha * add) % mod) ans ++ ; } cout << ans << ' '; return 0; }