题意
给你N个垃圾,垃圾桶在0号位置,在一个位置上捡起或丢掉任何数量的垃圾耗费是X,然后所带的垃圾数是K的话,那么走一步的耗费是
(K+1)2
(
K
+
1
)
2
,求最少耗费
1≤N≤200000
1
≤
N
≤
200000
分析
这道题还是挺神的啊
发现这个耗费是平方的,每一段路程的耗费不一样,斜率优化好像不是很可行
那肯定是一个贪心了,其实很快想到一个策略是每次跑到你想要的最后面的物体然后往前拿
然后呢。。。。
https://www.youtube.com/watch?v=xS4ZwwnHG1w
然后其实就是化简一下,假设要拿的位置是a,b,c,d,且在x轴上是递增的四个点
耗费就是
(a−0)+(b−a)+(c−b)+(d−c)+(d−c)∗4+(c−b)∗9+(b−a)∗16+(a−0)∗25
(
a
−
0
)
+
(
b
−
a
)
+
(
c
−
b
)
+
(
d
−
c
)
+
(
d
−
c
)
∗
4
+
(
c
−
b
)
∗
9
+
(
b
−
a
)
∗
16
+
(
a
−
0
)
∗
25
=5d+5c+7b+9a
=
5
d
+
5
c
+
7
b
+
9
a
发现有规律了,而且我们还能发现,所有5的点是在最后面连起来的,7的点也是连起来的。。。。。
这个可以用反证法证,如果不连起来的话,那么肯定前面的小的值要乘于坐标大的
然后枚举它跑了
k
k
次,每次往前跳最多下,总的时间复杂度是
O(nlogn)
O
(
n
l
o
g
n
)
注意判断边界
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N = 200100;
inline ll read()
{
ll p=0; ll f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
ll a[N],s[N];
ll calc(ll l,ll r){return s[r] - s[l-1];}
int main()
{
//freopen("a.in","r",stdin);
ll n = read(); ll x = read();
for(ll i=1;i<=n;i++) a[i] = read();
for(ll i=1;i<=n;i++) s[i] = s[i-1] + a[i];
ll minx = (ll)1e55;
for(ll k=1;k<=(n+1)/2;k++)
{
ll l = 0; ll r = n; ll ans = 0; ll v = 5;
for(ll i=1;r>=1;i++)
{
l = max(1ll , r - k + 1);
ans += calc(l,r) * v;
if(ans + k*x > minx) break;
r = l-1; if(i==1) continue;
else v+=2;
}
minx = min(minx , ans + k*x);
// printf("%lld %lld\n",k , ans + k*x);
}
return printf("%lld\n",minx + n*x),0;
}