CF2093G. Shorten the Array

题意 给定长度为 $n$ 的数组 $a$,从中找出两个数 $a_{i},a_{j}$ 满足 $a_{i}\oplus a_{j} \geqslant k$,并最大化两数距离(即最大化 $j-i+1$),输出这个最大距离。找不到则输出 $-1$。

思路 两数异或最大可以用字典树,但本题有个更好写的做法。

比大小问题,可以转化为判断相等。

给定 $b$,枚举 b += lowbit(b)(其本质是把 $b$ 的一个 0 变 1,低位都变 0)。如果 $a \geqslant b$,那么必然存在一个枚举结果 $b’$,满足 $a$ 同样位数变 0 后与 $b’$ 相同。

比如 $b$ 是 10110,一个数大于等于 $b$,只能是 1011?、11??? 或 1??? 这几种形式。而枚举 b += lowbit(b),刚好会得到 10110 $\to$ 11000 $\to$ 100000 $\to$ 1000000 $\to$ …… 满足 $a$ 低位都变 0 后与 $b’$ 相同。

这样,我们就把 $a \geqslant b$ 问题转化为判断 32 次 $a’=b’$。

回到本题,只需枚举 k += lowbit(k),同时把 $a_{i}$ 的低位都变 0,判断是否存在 $a_{i}‘\oplus a_{j}’ = k’$,用桶存下标即可。时间复杂度 $\operatorname{O}(n\log^{2} V)$,$V=10^{9}$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <bits/stdc++.h>
using namespace std;
constexpr int inf = 0x3f3f3f3f;

int main() {
int t;
cin >> t;

while (t--) {
int n, k;
cin >> n >> k;

vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}

int res = inf;
if (k == 0) {
res = 1;
}
for (; k > 0; k += (k & -k)) {
map<int, int> mp;
for (int i = 0; i < n; i++) {
int x = a[i] & ~((k & -k) - 1);
if (mp.count(k ^ x)) {
res = min(res, i - mp[k ^ x] + 1);
}
mp[x] = i;
}
}
cout << (res == inf ? -1 : res) << endl;
}

return 0;
}

练习2023 山东省赛 J. Not Another Path Query Problem


UPD:上面代码复杂度略高,TLE。可以用 vector 二分代替 map,实现 $\operatorname{O}(n\log n\log V)$ 的复杂度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
int main() {
int t;
cin >> t;

while (t--) {
int n, k;
cin >> n >> k;

vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}

int res = inf;
if (k == 0) {
res = 1;
}
for (; k > 0; k += k & -k) {
vector<pair<int, int>> mp(n);
for (int i = 0; i < n; i++) {
mp[i].first = a[i] & ~((k & -k) - 1);
mp[i].second = -i;
}
sort(mp.begin(), mp.end());
for (int i = 0; i < n; i++) {
int b = a[i] & ~((k & -k) - 1);
auto it = lower_bound(mp.begin(), mp.end(), pair(b ^ k, -i));
if (it == mp.end() || it->first != (b ^ k)) {
continue;
}
int j = -it->second;
res = min(res, abs(i - j) + 1);
}
}
cout << (res == inf ? -1 : res) << endl;
}

return 0;
}