CF2093G. Shorten the Array
题意 给定长度为n 的数组a,从中找出两个数ai,aj 满足ai⊕aj⩾k,并最大化两数距离(即最大化j−i+1),输出这个最大距离。找不到则输出−1。
思路 两数异或最大可以用字典树,但本题有个更好写的做法。
比大小问题,可以转化为判断相等。
给定b,枚举 b += lowbit(b)
(其本质是把b 的一个 0 变 1,低位都变 0)。如果a⩾b,那么必然存在一个枚举结果b′,满足a 同样位数变 0 后与b′ 相同。
比如b 是 10110,一个数大于等于b,只能是 1011?、11??? 或 1??? 这几种形式。而枚举 b += lowbit(b)
,刚好会得到 10110→ 11000→ 100000→ 1000000→ …… 满足a 低位都变 0 后与b′ 相同。
这样,我们就把a⩾b 问题转化为判断 32 次a′=b′。
回到本题,只需枚举 k += lowbit(k)
,同时把ai 的低位都变 0,判断是否存在ai′⊕aj′=k′,用桶存下标即可。时间复杂度O(nlog2V),V=109。
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) 的复杂度。
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; }
|