CF314A 题解
前言
这翻译明显错误,建议大家去原题面看。
简要题意
有一个序列 \(a_1, a_2, \cdots, a_n\),每次按照公式 \(d_i=\sum\limits_{j=1}^{i-1}\left[a_j \cdot (j-1)-(n-i) \cdot a_i\right]\) 计算。每次再找到最小的 \(x\) 满足 \(d_x < k\)。
将 \(a_x\) 删除,然后再来一轮。找不到 \(x\) 时停止。依次输出所有被删除的数原本的编号。
思路
我们想让 \(d_i\) 有承接关系,即,知道 \(d_{i-1}\) 能快速算出 \(d_i\)。
这一点还是比较容易的,把 \(a_i\) 的计算部分提出来即可。\(d_i=\left[\sum\limits_{j=1}^{i-1}a_j \cdot (j-1)\right] - \huge[\)\((i - 1) \cdot (n-i) \cdot a_i\huge]\)。
也就是说,我们可以计算:\(x_i = \sum\limits_{j=1}^{i-1}a_j \cdot (j-1)\),当前的 \(d_i\) 显然就是 \(x_i - (i - 1) \cdot (n-i) \cdot a_i\)。
这样,对于删除操作,我们就不必重新计算榜单了,如果删除掉 \(i\),只需要计算 \(x_i\) 时忽略掉它。
那么代码就十分容易了,直接模拟就行。时间复杂度显然是 \(O(n)\)。
实际可以直接用一个变量代替 \(x_i\),扫一遍就做完了。
完整代码
/*为了代码更通俗,使用了不关同步的 cin 与 cout。不保证在输入输出上会被卡常。*/
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int n, k, nowi = 0; //nowi是真正的i
cin >> n >> k;
long long d = 0; //由于每个di可以承接地算,所以直接扫一遍即可。删除就是不计算i的贡献入内
for (int i = 1; i <= n; i++)
{
nowi++;
int ai; cin >> ai;
if (d - 1ll * (i - 1) * (n - i) * ai < k) {write(nowi), endl; n--, i--;}
else d += 1ll * ai * (i - 1);
}
return 0;
}
希望能帮助到大家!