CF2093G. Shorten the Array

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

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

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

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

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

这样,我们就把aba \geqslant b 问题转化为判断 32 次a=ba'=b'

回到本题,只需枚举 k += lowbit(k),同时把aia_{i} 的低位都变 0,判断是否存在aiaj=ka_{i}'\oplus a_{j}' = k',用桶存下标即可。时间复杂度O(nlog2V)\operatorname{O}(n\log^{2} V)V=109V=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,实现O(nlognlogV)\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;
}