題目解法
倒過來考慮題目中的過程,即從全零數組開始,進行題目中操作的擬操作。
則應當每次找到數組中最低的爲 的位置 ,令 。
給定操作次數 ,考慮如何高效地還原出 次操作後的數組。
我們稱一次操作 爲操作 。
則第 次操作應當爲操作 ;
刪除所有操作 後,第 次操作應當爲操作 ;
刪除所有操作 後,則第 次操作應當爲操作 ……
因此,我們可以求出 次操作後,序列的長度 。
由於序列的長度並不確定,我們可以通過上述過程二分求出序列的長度。
不難發現,序列的長度 ,在 時, 。
事實上, 一篇論文 指出, 時,有 ,可以對 進行一個較爲精確的估計。
時間複雜度 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 5;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
ll k, ans[MAXN];
int check(int mid) {
int ans = 0; ll tmp = k + mid;
while (tmp != 0) {
ans++;
ll mns = tmp / (ans + 1) + (tmp % (ans + 1) != 0);
tmp -= mns;
}
return ans;
}
int main() {
int n, last; read(k);
if (k <= 1e11) {
int l = 1, r = 2e6;
while (true) {
int mid = (l + r) / 2;
int tmp = check(mid);
if (tmp == mid) {
l = r = mid;
break;
}
if (tmp > mid) l = mid + 1;
else r = mid - 1;
} n = l;
} else {
n = check(2e6), last = 2e6;
while (n != last) {
last = n;
n = check(n);
}
}
ll tmp = k + n;
printf("%d\n", n);
for (int i = 1; i <= n; i++) {
ll mns = tmp / (i + 1) + (tmp % (i + 1) != 0);
ans[1] -= mns, ans[i] += mns;
ans[i] += mns * i, ans[i + 1] -= mns * i;
tmp -= mns;
}
for (int i = 1; i <= n; i++) {
ans[i] += ans[i - 1];
printf("%lld ", ans[i]);
}
printf("\n");
return 0;
}