Luogu7948[✗✓OI R1] 前方之风
Luogu7948[✗✓OI R1] 前方之风
没想到这么久不见洛谷这么多题了。这是2021年11月月赛的题,蛮有意思的,在图书馆调了一晚上。
题面
给出一个长度为 \(n\) 的序列 \(a\) 和 \(q\) 个询问,第 \(i\) 个询问给出 \(k_i\)。对于每次询问,你需要进行以下操作:
1.求出剩下的数的平均数 \(\mathit{avg}\)。
2.将剩下的数中小于 \(\mathit{avg}-k_i\)的数删去。
3.重复以上两个步骤直到所有数都不会被删去。
4.输出最后会剩下几个数。
注意:询问之间是独立的,也就是说,不会真的删去那些数。
sov
force
对数组进行排序,计算前缀和,每次二分查找会被删除的数,时间复杂度\(O(nlogn + q \quad\sum_{i = 0}^{t}{logn})\),实测只差一个点不能过,但是他开了subtask。
std
我们发现,能够预处理\(x_i - x_n\)不删除数的最大\(k_i\), \(k_{imax} = \overline{x} - x_i\),这样我们就可以预处理每一段的\(k_i\)。
我们再考虑每次删除一定会是从小到大删除,如果这一次不能删除,则过程终止,因此数列\(\{k\}\)应该是单调递减的,\(k_i = \min(k_{i-1},k_i)\),对于每次询问,在预处理的\(k\)数组中进行二分查询。
时间复杂度\(O(n\log{n} + q\log{n})\)
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;
double a[MAXN], sum[MAXN], ave[MAXN];
int n, q, t;
signed main()
{
scanf("%d", &t);
while (t--)
{
scanf("%d %d", &n, &q);
for (register int i = 1; i <= n; ++i)
scanf("%lf", &a[i]);
sort(a + 1, a + 1 + n);
for (register int i = 1; i <= n; ++i)
sum[i] = sum[i - 1] + a[i];
ave[0] = LONG_LONG_MAX;
for (register int i = 1; i <= n; ++i)
{
ave[i] = (sum[n] - sum[i - 1]) / (n - i + 1) - a[i];
ave[i] = min(ave[i], ave[i - 1]);
}
while (q--)
{
int k;
scanf("%d", &k);
int tmp = (lower_bound(ave + 1, ave + n + 1, k, greater<double>()) - ave - 1);
printf("%d ", n - tmp);
}
puts("");
}
}