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;
}

希望能帮助到大家!

posted @ 2022-09-14 17:10  liangbowen  阅读(17)  评论(0编辑  收藏  举报