poj3709 K-Anonymous Sequence[贪心+斜率优化dp]

地址

n个数,可进行把一个数减小的操作,代价为减小的值。现求使数列任意一个数都存在至少k-1个数和他相同,问操作的最小代价。


 

可以先考虑最小的数,由于只能减,所以必须得至少k-1个数减为最小数,贪心策略:从小到大从最小数开始的后面至少k-1个数必须减为他自己这一块代价才最小。很好想,如果里面有一个不选,那必须有一个更大的数下降,并且不选的这个数在之后也使后面另一块的数减的更多,所以总是把连续的至少k个数减为开头最小的那个数。那就是数列上划分块的dp,$f[i]$是到$i$时最小代价。

$f[i]=min \{ f[j]+sum[i]-sum[j]-(i-j)*a[j+1] \} $             $  0<=j<=i-k且j∉[1,k-1]$

然后拆开就是一个常规的斜率优化了。注意一下开头k-1个是不能作为决策点的(因为无解),不要进队。0是可以进队的。

没了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 typedef long long ll;
 8 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
 9 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
10 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
11 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
12 template<typename T>inline T read(T&x){
13     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
14     while(isdigit(c))x=x*10+c-'0',c=getchar();return f?x=-x:x;
15 }
16 const int N=500000+7;
17 ll f[N],sum[N];
18 int a[N],q[N],T,n,k,l,r;
19 inline ll x(int j){return (ll)a[j+1];}
20 inline ll y(int j){return f[j]+j*1ll*a[j+1]-sum[j];}
21 
22 int main(){//freopen("test.in","r",stdin);//freopen("tmp.out","w",stdout);
23     read(T);while(T--){
24         read(n),read(k);l=1,r=0;
25         for(register int i=1;i<=n;++i)sum[i]=read(a[i])+sum[i-1];
26         for(register int i=k;i<=n;++i){
27             if(i==k||i>=(k<<1)){
28                 while(l<r&&(y(i-k)-y(q[r]))*(x(q[r])-x(q[r-1]))<=(y(q[r])-y(q[r-1]))*(x(i-k)-x(q[r])))--r;
29                 q[++r]=i-k;
30             }
31             while(l<r&&y(q[l+1])-y(q[l])<=1ll*i*(x(q[l+1])-x(q[l])))++l;
32             f[i]=f[q[l]]+sum[i]-sum[q[l]]-(i-q[l])*1ll*a[q[l]+1];
33         }
34         printf("%lld\n",f[n]);
35     }
36     return 0;
37 }

 

posted @ 2019-03-02 21:29  Ametsuji_akiya  阅读(161)  评论(0编辑  收藏  举报