一道很好的构造题
原题链接
很快就能想到,捡每个垃圾的能量可以最后再算。然后,对于每个垃圾,在路上耗费的能量仅与它是第几个被捡的有关,于是我们考虑将垃圾分组。
首先,我们定义\(F(x,i)\)为某次从\(0\)出发,捡到坐标为\(x\)的垃圾的次序为\(i\)的花费,则有:
由上式,易知对于每一组,让机器人先捡最右边的垃圾,然后往回一个一个捡是最优的。同时,将所有垃圾从后往前按一个固定的间隔 \(k\)分组是最优的。
所以,我们只需要枚举 \(k\),再利用前缀和, \(O(logn)\)计算组间距为 \(k\)的花费,更新一下答案就行了。
为什么是 \(O(logn)\)?因为调和级数的性质, \(\sum\limits_{i=1}^n\frac{1}{i}=logn+O(1)\)。
因为中途计算时可能会爆 \(long long\),所以我们要特判一下,当前的花费大于等于 \(ans\)时就可以跳出循环了。
Code:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n;
ll x, ans = (ll)9e18, a[200005];
int main() {
cin >> n >> x;
for(int i = 1; i <= n; ++i) cin >> a[i], a[i] += a[i-1];
for(int k = 1; k <= n; ++k) {
ll coef = 3LL, sum = 0;
for(int i = n; i >= 1; i -= k) {
sum += (a[i]-a[max(0, i-k)])*max(coef, 5LL), coef += 2;
if(sum >= ans) break; //防爆long long
}
ans = min(ans, sum+(k+n)*x);
}
cout << ans << endl;
return 0;
}