从CF1901C学习除二向下取整的思路

https://codeforces.com/problemset/problem/1901/C

我发现对于向下取整向上取整的题目(不特指除二),没有一些常见的思路积累。

Description

给定一个长度为 \(n\) 的序列 \(\{a_n\}\)。每次操作你需要选择一个整数 \(x\) 并将所有 \(a_i\) 替换为 \(\lfloor \frac {a_i + x}2 \rfloor\)。求至少多少次操作后能将所有 \(a_i\) 变相同。

若最少次数小于等于 \(n\),输出操作次数和每次操作所选择的 \(x\)。否则仅输出操作次数。

\(1 \le n \le 2 \times 10^5\)\(0 \le a_i \le 10^9\)

Solution

可以观察到,每次操作后,数字的相对大小关系不会发生改变。

例如 \(a = \{4, 2, 5\}\)\(x = 3\),那么操作后的 \(a' = \{3, 2, 4\}\)。可以发现 \(a_3\)\(a'_3\) 分别都是两个序列的最大值,\(a_2\)\(a'_2\) 都是两个序列的最小值。

那么就启发我们不用同时维护原序列中的所有值,只需要维护序列中的最大值和最小值。当这两个值相等时,就代表序列中所有的数都相同了。

那么问题就被转化成了这样:

给定两个整数 \(a, b\)。每次操作你需要选择一个整数 \(x\) 并将 \(a \gets \lfloor \frac {a + x}2 \rfloor\)\(b \gets \lfloor \frac {b + x}2 \rfloor\)。求至少多少次操作后 \(a = b\)

每次操作我们肯定是希望利用下取整这个优秀性质来让 \(a\) 尽量接近 \(b\),也就是想让操作后 \(a - b\) 尽量小。那么我们可以直接分讨:

然后就解决了。用 vector 记录答案即可。

Code

void solve()
{
	cin >> n;
	a = 0, b = 2e9;
	for (int i = 1; i <= n; ++ i )
	{
		cin >> x;
		a = max(a, x);
		b = min(b, x);
	}
	
	vector<int> res;
	while (a != b)
	{
		if (a % 2 == 0 && b % 2 == 0) x = rand();
		else if (a % 2 == 1 && b % 2 == 1) x = rand();
		else if (a % 2 == 0 && b % 2 == 1) x = rand() * 2 + 1;
		else x = rand() * 2;
		a = (a + x) / 2;
		b = (b + x) / 2;
		res.push_back(x);
	}
	
	cout << res.size() << '\n';
	if (res.size() <= n)
	{
		for (auto i : res) cout << i << ' ';
		if (res.size()) puts("");
	}
	
	return;
}

题解的四个超链接很重要,对于除以二的向下取整的值,可以直接把奇数的转成减一除二

\(a\) 为偶数,\(b\) 为偶数

\(\Delta\) 为操作后 \(a\)\(b\) 的差。

  • 若选择的 \(x\) 为偶数: \(a' = \dfrac {a + x} 2\)\(b' = \dfrac {b + x}2\)\(\Delta = \dfrac {a - b} 2\)。 -
  • 若选择的 \(x\) 为奇数: \(a' = \dfrac {a + x - 1} 2\)\(b' = \dfrac {b + x - 1} 2\)\(\Delta = \dfrac {a - b}2\)
  • 可以发现,无论 \(x\) 怎样取值,两数差一定为 \(\dfrac {a - b}2\)。因此 \(x\) 任意取值。

Solution

\(x\) 对于除以 \(k\) 上取整

sum = (x + k - 1) / k
posted @ 2024-01-21 21:39  加固文明幻景  阅读(20)  评论(0编辑  收藏  举报