题目链接
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 << '\n';
return 0;
}