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;
}
希望能帮助到大家!