CF1758D 题解

前言

题目传送门!

更好的阅读体验?

一种比较麻烦的做法,比较莽,和题解区不一样,但是方法困难很多。

思路

很自然的想法是让 \(a_n-a_1=n\),这样整个序列会相对比较连续,好处理。

首先可以尝试 \(x+1, x+2, \cdots, x+n\) 的填法。发现不能完美填充(也就是还需要额外地补),但是为了保持 \(a_n-a_1=n\),无法操作。

所以考虑 \(x, x+2, x+3, \cdots, x+n\) 的填法,这样子 \(2 \sim n\) 都可以往前补,容错更大(可以自行模拟)。

然后计算一下合适的 \(x\)

\(\because n = \sqrt{x + \dfrac{(2x+n+2)(n-1)}{2}}\)

\( \begin{aligned} \therefore n^2 & = x + \dfrac{(2x+n+2)(n-1)}{2} \\ & = x + \dfrac{2xn-2x+n^2-n+2n-2}{2} \\ & = xn + \dfrac{n^2}{2} + \dfrac{n}{2} - 1 \\ \end{aligned} \)

\(\therefore xn= n^2 - \dfrac{n^2}{2} - \dfrac{n}{2} + 1 = \dfrac{n^2-n+2}{2}\)

\(\therefore x = \dfrac{n^2-n+2}{2n} = \dfrac{n}{2}-\dfrac{1}{2}+\dfrac{1}{n}\)

这个就是理论上刚刚好的 \(x\),由于它要是整数,采取向上取整。

然后就可以写出一个看起来没有问题的代码:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n, ans[300005];
long long calc()
{
	long long sum = 0;
	for (int i = 1; i <= n; i++) sum += ans[i];
	return sum - 1ll * (ans[1] - ans[n]) * (ans[1] - ans[n]);
}
void solve()
{
	scanf("%d", &n);
	if (n == 3) {puts("3 6 7"); return;}
	int x = ceil(n / 2.0 - 1 / 2.0 + 1.0 / n);
	ans[1] = x; for (int i = 2; i <= n; i++) ans[i] = x + i;
	long long sum = calc();
	for (int i = 2; i < n; i++) //顺着填补
		if (sum != 0) sum--, ans[i]--;
		else break;


	if (sum != 0) printf("Emmmm...... n = %d false, sum = %d\n", n, sum); //check


	for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
	puts("");
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--) solve();
	return 0;
}

然后发现 \(\operatorname{check}()\) 标记的位置,在 \(n\) 为奇数时,总是会显示答案差了个 \(1\),但是已经没有位置补了。

(好像所有 \(n\) 为奇数时,\(sum\) 都是 \(1\),没有去证明,有没有老哥帮忙填一下这个坑?)

这个时候会有点崩溃,所以开始乱搞。我们对整个数组的所有元素翻倍。

这个时候 \((a_1-a_n)^2\) 总是要大于 \(\sum\limits_{i=1}^n a_i\) 的,所以通过整体补的方式,让差值小于 \(n\)

此时数组的相邻元素差控制在 \(2\) 左右(边界可能比较特殊),所以直接按照上面代码的方式填就行,只不过是倒着填,以及 \(ans_i\) 要增加而已。

这样子的容错大约是 \(2n\)(应该要小一点),但是容下 \(n\) 是绰绰有余的,所以就做完了。

代码

重构了代码,应该很好理解!

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n, ans[300005];
long long calc()
{
	long long sum = 0;
	for (int i = 1; i <= n; i++) sum += ans[i];
	return sum - 1ll * (ans[1] - ans[n]) * (ans[1] - ans[n]);
}
void solve()
{
	scanf("%d", &n);
	if (n == 2) {puts("1 3"); return;} //特判
	if (n == 3) {puts("3 6 7"); return;}
	int x = ceil(n / 2.0 - 1 / 2.0 + 1.0 / n);
	ans[1] = x; for (int i = 2; i <= n; i++) ans[i] = x + i; //这里就是按照上面的方法乱搞
	for (int i = 1; i <= n; i++) ans[i] <<= 1;
	long long sum = -calc();
	for (int i = 1; i <= n; i++) ans[i] += (sum / n);
	sum %= n;
	
	for (int i = n - 1; i >= 2; i--)
		while (ans[i + 1] != ans[i] + 1) //倒着填补
		{
			if (sum != 0) ans[i]++, sum--;
			else break;
		}
	for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
	puts("");
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--) solve();
	return 0;
}

希望能帮助到大家!

posted @ 2023-04-13 18:10  liangbowen  阅读(27)  评论(0编辑  收藏  举报