BZOJ3437: 小P的牧场

【传送门:BZOJ3437


简要题意:

  一条线上有n个点,每个点都有一个权值,每个点都可以选择放置一个监测器,假设i点有监测器,那么假设i点左边最靠近i点的监测器在j点,那么i点的监测器就可以监测j+1到i的所有点

  每个点放置监测器都有花费(这个花费不是点的权值),而且假设在y点放置了监测器,并且y点的监测器监测了x到y的所有点,那么除了放置监测器的花费之外,还需要将x到y-1里的点消耗额外的花费:设k点在x到y-1的点中,那么k点会消耗y点与k点之间的点数(不算k点,但是算y点)乘上k的权值

  要求能监测到n个点(也意味着第n个点必须要放监测器)的最小费用


题解:

  DP,数据范围太大只能DP

  f[i]表示在第i个位置放置监测器的最小费用,显然直接DP会T

  那么就斜率优化

  f[i]=min(f[j]+a[i]+(i-(j+1))*b[j+1]+(i-(j+2))*b[j+2]...)

  其实可以发现

  f[i]=min(f[j]+a[i]+i*b[j+1]-(j+1)*b[j+1]+i*b[j+2]-(j+2)*b[j+2]...)

  f[i]=min(f[j]+a[i]+i*(b[j+1]+b[j+2]+...)-(j+1)*b[j+1]-(j+2)*b[j+2]...)

  设sb[i]=b[1]+b[2]+...+b[i]

  cb[i]=b[1]*1+b[2]*2+b[3]*3...+b[i]*i

  然后就得到f[i]=min(f[j]+a[i]+i*(sb[i-1]-sb[j])-(cb[i-1]-cb[j]))

  接着化简斜率方程,设j1<j2<i

  (f[j2]-f[j1]+cb[j2]-cb[j1])/(sb[j2]-sb[j1])<i的时候,淘汰j1

  然后A了,要加long long


参考代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
LL f[1100000];
LL a[1100000],b[1100000];
LL sb[1100000],cb[1100000];
//f[i]=min(f[j]+a[i]+i*(sb[i-1]-sb[j])-(cb[i-1]-cb[j]))
double slop(int j1,int j2)//(f[j2]-f[j1]+cb[j2]-cb[j1])/(sb[j2]-sb[j1])<i
{
    return (f[j2]-f[j1]+cb[j2]-cb[j1])/(sb[j2]-sb[j1]);
}
int list[1100000];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    sb[0]=0;cb[0]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&b[i]);
        sb[i]=sb[i-1]+b[i];
        b[i]*=i;
        cb[i]=cb[i-1]+b[i];
    }
    int head=1,tail=1;list[1]=0;
    for(int i=1;i<=n;i++)
    {
        while(head<tail&&slop(list[head],list[head+1])<double(i)) head++;
        int j=list[head];
        f[i]=f[j]+a[i]+i*(sb[i-1]-sb[j])-(cb[i-1]-cb[j]);
        while(head<tail&&slop(list[tail-1],list[tail])>slop(list[tail],i)) tail--;
        list[++tail]=i;
    }
    printf("%lld\n",f[n]);
    return 0;
}

 

posted @ 2017-11-23 14:00  Star_Feel  阅读(288)  评论(0编辑  收藏  举报