AtCoder Grand Contest 027 B - Garbage Collector 贪心

题意

给你N个垃圾,垃圾桶在0号位置,在一个位置上捡起或丢掉任何数量的垃圾耗费是X,然后所带的垃圾数是K的话,那么走一步的耗费是 (K+1)2 ( K + 1 ) 2 ,求最少耗费
1N200000 1 ≤ N ≤ 200000

分析

这道题还是挺神的啊
发现这个耗费是平方的,每一段路程的耗费不一样,斜率优化好像不是很可行
那肯定是一个贪心了,其实很快想到一个策略是每次跑到你想要的最后面的物体然后往前拿
然后呢。。。。
https://www.youtube.com/watch?v=xS4ZwwnHG1w
然后其实就是化简一下,假设要拿的位置是a,b,c,d,且在x轴上是递增的四个点
耗费就是
(a0)+(ba)+(cb)+(dc)+(dc)4+(cb)9+(ba)16+(a0)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 次,每次往前跳最多n/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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值