A
B
- **猜结论题,直接卡死俩小时,大号卡成小号了(其实还是自己菜唉唉)
C
- 实际上就是把整个数组划成三段,而三个人对这三段的占有情况可以用全排列函数生成一下,如果发现最后的cur大于数组长度就可以标为不合法。
D
- 卡死B后最后剩了半小时看了下,蛮好做的,但是也挺结论,可以发现这个交换的策略和数组具体元素一点关系没有,我们完全可以认为a数组是一个1~n的全排列数组,那么用pb数组表示b数组映射到a数组上对应到a数组的下标,浅猜了一下,发现其实就是查pb数组中逆序对的个数,赛后两分钟交了两发,一发RE一发过了。
F
字典树解决区间最大异或对问题
- 我们考虑用字典树记录数组中每个数对应的二进制串,之后依次遍历数组中每一个数,寻找其能够与数组中另一个数能够异或出的最大值。
- 本题构建出的trie树和经典trie树大体一致,需要注意的是由于重复数字不会影响答案,所以我们不需要cnt数组来存储节点对应插入次数。
- find函数使用了贪心的思想,我们从高位往低位在trie树里搜索,一旦当前位能够异或出1(即
trie[now][t ^ 1] != 0,我们就可以有now = trie[now][t ^ 1],显而易见这种选择总是最优局面的决策。
- 例题:最大异或对 The XOR Largest Pair
- 例题代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <list>
#include <random>
#include <bitset>
#include <tuple>
#include <iterator>
#define int long long
typedef std::pair<int, int> PII;
std::mt19937 rd(114514);
const int N = 1e5 + 10;
int mvx[4] = {1, -1, 0, 0}, mvy[4] = {0, 0, 1, -1};
int n, a[N];
int idx = 0;
int trie[N * 32][2];
void insert(int x) {
int now = 0;
for (int i = 31; i >= 0; i --) {
int t = (x >> i) & 1;
if (!trie[now][t]) {
trie[now][t] = ++idx;
}
now = trie[now][t];
}
}
int find(int x) {
int now = 0, ans = 0;
for (int i = 31; i >= 0; i --) {
int t = (x >> i) & 1;
if (trie[now][t ^ 1] != 0) {
ans |= (1LL << i);
now = trie[now][t ^ 1];
} else {
now = trie[now][t];
}
}
return ans;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cin >> n;
for (int i =1 ;i <= n; i ++) {
int x;
std::cin >> x;
a[i] = x;
insert(x);
}
int max = 0xc0c0c0c0c0c0c0c0;
for (int i = 1; i <= n; i ++) {
max = std::max(find(a[i]), max);
}
std::cout << max << std::endl;
return 0;
}
题解部分
- 看到求第k大的The value of a sub-array,很自然想到二分。
- 我们在check函数中要求的其实是小于等于mid的The value of a sub-array有几个。我们发现按照题面对于The value of a sub-array的定义,如果我们固定住一个r,那么随着l的减小,这个子区间的The value of a sub-array是非严格递减的。所以我们可以遍历a数组,对于a[i],找到它对应的The value of a sub-array小于等于mid的所有子区间中最大的l,为了完成这个find操作,我们需要在每一个a[i] insert时往insert函数中传入一个时间戳参数,用一个dp数组记录每个节点的最近更新的时间。最后需要注意的是当我们遍历f数组,也就是每个r对应的最大l时,由于a[r]并不一定是The value of a sub-array的\(a_i\)和\(a_j\)中的其中之一,我们需要记录f[i]为f数组在1~i的最大值。
- 求出所有子区间中The value of a sub-array小于等于mid的子区间个数sum后,我们需要注意一下二分的逻辑:按照题意我们要求的是第k大个The value of a sub-array,我们是按值二分的,那么这个要求就可以等价为sum 大于等于k的mid集合中的最小值。