ABC281E 题解

· · 题解

打完去讨论区看了一下,大家这题都打的平衡树有关算法。

实际上,本题暴力 vector 插入可过。

以下做法可通过赛时数据,不保证会被 hack 掉。

思路

暴力也不是纯暴力,想到类似一个滑动窗口的方法:

对于 i \le m 的情况,由于不需要删除元素(后面会讲),所以直接暴力 insert 即可。

对于 $i > m$ 的情况,如下图所示: ![](https://cdn.luogu.com.cn/upload/image_hosting/tzxd8syv.png) 现在我们要加入新的元素 $val_i$(相当于区间滑动了一格)。 这里会有两种情况。情况一: ![](https://cdn.luogu.com.cn/upload/image_hosting/9e94naqy.png) 这种情况不需要做任何事,只用暴力插入。 情况二: ![](https://cdn.luogu.com.cn/upload/image_hosting/9nnc05qv.png) 这种情况,$sum$ 变为 $sum + val_i - a_k$。 --- 那么除了插入,还有删除呢!我们要删除 $val_{i - m}$。 这个是同理的,就不画图了: + 如果对答案没有影响,直接删除即可。 + 如果有影响,$sum$ 变为 $sum - a_{pos} + a_{k + 1}$,即删除元素并容下备选的元素。 ## 代码 为何暴力 `vector` 插入跑得飞快呢? + 数据范围较小,只有 $2\times 10^5$。 + 不是 Ynoi 题,数据也没有卡到极限数据(?)。 + `vector` 插入、删除,**常数很小很小**。 有不少题(如 [P1627](https://www.luogu.com.cn/problem/P1627))都是可以暴力 `vector` 过掉的。 ```cpp int val[200005]; void solve() { vector <int> a; a.pb(-1); //vector 下标从 0 开始,但是我习惯下标从 1 开始 int n, m, k; ll sum = 0; //注意 long long scanf("%d%d%d", &n, &m, &k); for (int i = 1; i <= n; i++) { scanf("%d", &val[i]); if (i <= m) //还不需要删除,直接暴力 insert { a.insert(lower_bound(a.begin(), a.end(), val[i]), val[i]); if (i == m) {for (int j = 1; j <= k; j++) sum += a[j];} //计算一下 sum,方便滑动 } else { int pos = lower_bound(a.begin(), a.end(), val[i]) - a.begin(); //插入的位置 if (pos <= k) sum += val[i] - a[k]; //如果对答案有影响,加上 val[i] 并挤掉当前的 a[k] a.insert(a.begin() + pos, val[i]); //暴力插入 pos = lower_bound(a.begin(), a.end(), val[i - m]) - a.begin(); //删除的位置 if (pos <= k) sum += - a[pos] + a[k + 1]; //如果对答案有影响,减去 a[pos] 并容下当前的 a[k+1] a.erase(a.begin() + pos); //暴力删除 } if (i >= m) printf("%lld ", sum); //输出答案即可 } puts(""); } ```