标签:操作 mes printf name div 防御 数组 scanf 必须
给出n个点,每个点对其进行两种操作:第一种将这个点变为特殊点,花费为a[i],第二种将这个点变为普通点,花费为右边最接近的特殊点与这个点的距离(比如说当前点为i点,右边最接近的点为j点,那么花费为(j-i)),这样就说明最右边的点必须为特殊点
求出将每个点都进行操作的最小花费
一开始想的是贪心,但是WA了,很懵,只好用DP
因为逆推很麻烦,就将a数组翻转,这样的话找左边的最接近的特殊点就可以了
得到方程:f[i]=min(f[j]+(i-j)*(i-j-1)/2+a[i])
因为两个特殊点之间的所有普通点的左边最接近的特殊点就是j,所以总花费为1+2+3+...+(i-j-1)=(i-j)*(i-j-1)/2
然后用斜率优化
列出斜率方程为:(j1<j2<i)
(2*(f[j2]-f[j1])+(j2^2-j1^2)+(j2-j1))/(j2-j1)<2*i
然后做就好了
PS:要加long long,注意i和j也要,因为j2^2,j1^2会爆int(这个问题WA了我七次,吐血)
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; LL f[1100000],a[1100000]; //f[i]=min(f[j]+(i-j-1)*(i-j)/2+a[i]) //f[i]=min(f[j]+(i-j-1)*(i-j)/2)+a[i] /* j1<j2<i f[j2]+(i-j2-1)*(i-j2)/2<f[j1]+(i-j1-1)*(i-j1)/2 2*(f[j2]-f[j1])+(i-j2-1)*(i-j2)<(i-j1-1)*(i-j1) 2*(f[j2]-f[j1])+i*i-2*i*j2+j2^2-i+j2<i*i-2*i*j1+j1^2-i+j1 2*(f[j2]-f[j1])+(j2^2-j1^2)+(j2-j1)<2*i*j2-2*i*j1 2*(f[j2]-f[j1])+(j2^2-j1^2)+(j2-j1)<2*i*(j2-j1) (2*(f[j2]-f[j1])+(j2^2-j1^2)+(j2-j1))/(j2-j1)<2*i */ LL slop(LL j1,LL j2) { return (2*(f[j2]-f[j1])+(j2*j2-j1*j1)+(j2-j1))/(j2-j1); } int list[1100000]; int main() { int n; scanf("%d",&n); for(int i=n;i>=1;i--) scanf("%lld",&a[i]); int head=1,tail=1; list[1]=1;f[1]=a[1]; for(LL i=2;i<=n+1;i++) { while(head<tail&&slop(list[head],list[head+1])<2*i) head++; LL j=list[head]; f[i]=f[j]+(i-j-1)*(i-j)/2+a[i]; while(head<tail&&slop(list[tail-1],list[tail])>slop(list[tail],i)) tail--; list[++tail]=i; } printf("%lld\n",min(f[n],f[n+1])); return 0; }
标签:操作 mes printf name div 防御 数组 scanf 必须
原文地址:http://www.cnblogs.com/Never-mind/p/7928571.html