【传送门: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 ",f[n]); return 0; }