给定一个长度为 \(n\) 的序列 \(a\)。挑出两个数 \(a_x,a_y(x\neq y)\) 使得让 \(a_x=a_y\) 的最小操作数最大。
一次操作是选择一个非负整数 \(k\),使得 \(2^k\geq a_i\),然后令 \(a_i\leftarrow 2^k-a_i\)。
\(2\le n\le 2\cdot 10^5,0\le a_i\le 10^9\)。
有史以来最简单的 \(\color{red}{\text{Div2 }}\color{purple}{\text{E}}\)。
首先可以考虑若已知 \(a_x,a_y\),让 \(a_x=a_y\) 大概是怎样的操作。将这个操作对应到二进制上相当于是将 \(\text{lowbit}(a_i)\) 前面的位全部取反,且操作是可逆的。
操作过程即是 \(a_x\) 先不断变小一段\((\)可能为空\()\),再不断变大一段\((\)可能为空\()\)。
不妨设 \(a_x>a_y\),若 \(a_x\) 二进制中最高位为 \(1\) 但在 \(a_y\) 中却没有此位那么显然 \(k\) 不会取到最高位前面去,不然就会产生多余的 \(1\),还需要额外的操作来抵消。
由于操作可逆,可以考虑建图,只需要考虑每个 \(a_i\) 变成 \(0\) 的过程。此时每次操作必定会用到最小的 \(k\) 满足 \(2^k\ge a_i\),再对 \(a_i\) 进行操作。在操作前后的 \(a_i\) 之间建边,单个数的操作数是 \(O(\log a_i)\) 的,所以总点数为 \(O(n\log \max a_i)\)。
于是题目转化成找这 \(n\) 个点在树上两两距离的最大值,两遍 \(dfs\) 即可。时间复杂度 \(O(n\log \max a_i)\)。
\(\color{blue}{\text{code}}\)
#include <bits/stdc++.h> using namespace std; const int N = 6e6 + 5, M = 2e5 + 5; int n, tot, a[M], dep[N]; vector <int> G[N]; unordered_map <int, int> mp; inline int op(int x) { int pw = log2(x); if ((1 << pw) >= x) return (1 << pw) - x; return (1 << pw + 1) - x; } inline void dfs(int x, int fa) { dep[x] = fa ? dep[fa] + 1 : 0; for (auto y : G[x]) if (y ^ fa) dfs(y, x); } int main() { scanf("%d", &n), mp[0] = tot = 1; for (int i = 1; i <= n; ++ i) { scanf("%d", a + i); int p = a[i]; if (mp[p]) continue; mp[p] = ++ tot; int nxt = op(p); while (!mp[nxt]) { mp[nxt] = ++ tot, G[mp[nxt]].emplace_back(mp[p]), G[mp[p]].emplace_back(mp[nxt]); p = nxt, nxt = op(p); } G[mp[nxt]].emplace_back(mp[p]), G[mp[p]].emplace_back(mp[nxt]); } dfs(mp[a[1]], 0); int p = 1; for (int i = 2; i <= n; ++ i) if (dep[mp[a[i]]] > dep[mp[a[p]]]) p = i; dfs(mp[a[p]], 0); int q = 1; for (int i = 2; i <= n; ++ i) if (dep[mp[a[i]]] > dep[mp[a[q]]]) q = i; return printf("%d %d %d\n", p, q, dep[mp[a[q]]]), 0; }