Codeforces 1617E. Christmas Chocolates (~2400)

题目描述

给定一个长度为 \(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;
}
上一篇:Codeforces Round #761 (Div. 2)


下一篇:Codeforces Round #759