AGC027B Garbage Collector

一道很好的构造题
原题链接
很快就能想到,捡每个垃圾的能量可以最后再算。然后,对于每个垃圾,在路上耗费的能量仅与它是第几个被捡的有关,于是我们考虑将垃圾分组。
首先,我们定义\(F(x,i)\)为某次从\(0\)出发,捡到坐标为\(x\)的垃圾的次序为\(i\)的花费,则有:

$F(x,i)=\left\{\begin{matrix} 5x\ \ (i=1)\\ (2i-1)x\ \ (i>1) \end{matrix}\right.$
由上式,易知对于每一组,让机器人先捡最右边的垃圾,然后往回一个一个捡是最优的。同时,将所有垃圾从后往前按一个固定的间隔$k$分组是最优的。 所以,我们只需要枚举$k$,再利用前缀和,$O(logn)$计算组间距为$k$的花费,更新一下答案就行了。 为什么是$O(logn)$?因为调和级数的性质,$\sum\limits_{i=1}^n\frac{1}{i}=logn+O(1)$。 因为中途计算时可能会爆$long long$,所以我们要特判一下,当前的花费大于等于$ans$时就可以跳出循环了。 Code: ``` cpp #include

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

posted @ 2018-09-17 09:36  dummyummy  阅读(327)  评论(0编辑  收藏  举报