POJ 3709 K-Anonymous Sequence 斜率DP
K-Anonymous Sequence POJ - 3709
给一个不降的序列,以及一个数 ,对这个序列进行操作,允许对某些数减去一定的值,要求操作之后的序列的每种取值的个数至少为 ,定义花费为修改前后每个数的绝对值的和,求最小花费。
令 表示前 个数的最小花费,则转移方程为:
其中 为序列中第 个元素的值, 为前缀和,这个式子的意思是前 个数的最小花费为前 个数的最小花费加上第 个数到第 个数都改成 的花费。
考虑斜率DP,设 ,且 优于 ,则有:
其中 。
代码如下(注意在队列尾进行判断的时候此处必须是 ):
#include<iostream>
#include<cstdio>
#include<cstring>
//#define WINE
#define INF 0x3f3f3f3f
#define MAXN 500100
using namespace std;
typedef long long ll;
int n,k,T,q[MAXN],h,t;
ll s[MAXN],dp[MAXN],a[MAXN];
ll up(int j,int k){
return dp[j]-s[j]+j*a[j+1]-(dp[k]-s[k]+k*a[k+1]);
}
ll down(int j,int k){
return a[j+1]-a[k+1];
}
ll getDP(int k,int i){
return dp[k]+s[i]-s[k]-a[k+1]*(i-k);
}
int main(){
#ifdef WINE
freopen("data.in","r",stdin);
#endif
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
s[i]=s[i-1]+a[i];
}
h=t=0;q[t++]=0;
for(int i=k;i<=n;i++){
while(h+1<t&&up(q[h+1],q[h])<i*down(q[h+1],q[h]))
h++;
dp[i]=getDP(q[h],i);
int j=i-k+1;
if(j<k)continue;
while(h+1<t&&up(j,q[t-1])*down(q[t-1],q[t-2])<=up(q[t-1],q[t-2])*down(j,q[t-1]))
t--;
q[t++]=j;
}
printf("%lld\n",dp[n]);
}
return 0;
}