异或哈希
应用场景
一般用于快速找到一个子数组是否完整地出现,主要是利用了异或的性质和哈希降低冲突的原理 .
原理介绍
异或满足交换律,判断某一段数字是否一一出现(排序后相同),可以通过其区间异或值是否相同判定 .
区间异或值需要先维护前缀异或和,区间 $[l,r]$ 的区间异或值为$Sum[r] \oplus Sum[l-1]$ .
代码模版
例:判断区间[l,r]的数字是否包含数字 $l - r$1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include<bits/stdc++.h>
#define endl '\n'
using i64 = long long;
using namespace std;
mt19937_64 rnd(time(0)); //随机数生成
int n, q, l,r,a[300010];
i64 base[300010], Xor[300010], Hash[300010];
signed main(){
std::ios::sync_with_stdio(false);cin.tie(0);
cin >> n >> q;
for (int i = 1; i <= n;i++)
base[i] = rnd(), Xor[i] = Xor[i - 1] ^ base[i];
for (int i = 1; i <= n; i++)
cin >> a[i], Hash[i] = Hash[i - 1] ^ base[a[i]];
while(q--){
int l, r;
cin >> l >> r;
cout<<(Hash[r] ^ Hash[l-1] == Xor[r] ^ Xor[l-1] ?" Yes" : "No) << endl;
}
if(Hash)
return 0;
}
其中,mt19937_64 rnd(time(0))是C++ \
题目链接:https://codeforces.com/contest/1175/problem/F
题意
给定一个$a$数组,如果该数组的某个子数组 $a_l,a_{l+1}…,a_{r-1},a_r$ 包含从1到$r-l+1$的所有整数有且只有一次,那么该子数组称之为”子排列”。请计算子排列的数量 .
数据范围
- $1 \le n \le 3 \cdot 10^5$
- $1 \le a_i \le n$
分析
不难发现,一个区间子排列必然包含1和子区间最大值;假设最大值在$1$的右侧,当我们枚举到各点的最大值(右端点)时,最大值就是该区间的长度,所以我们可以推断出理论的区间左端点,设当前最大值为$mx$,当前下标为$i$,上一次出现$1$的位置是$cur$条件是:
- $cur \ge i-mx+1$ ,即左端点一定要包含最近的$1$
- 区间$[l,r]$对应的$[1 , r-l+1]$数值恰好出现一次,套用异或哈希的模版即可
上述便是最大值出现在$1$右侧的情况;反之,我们将数组翻转(reverse),再进行一次遍历就能统计出最大值出现在$1$左侧的情况。如若满足两者条件,计数值加一,并记得特判$a_i=1$本身同样是子排列 .
Code:
1 | #include<bits/stdc++.h> |


