POJ 3709 K-Anonymous Sequence(斜率优化DP)
【题目链接】 http://poj.org/problem?id=3709
【题目大意】
给出一个长度为n个非严格单调递增数列,每次操作可以使得其中任意一项减一,
问现在使得数列中每项数相同的数的数量都大于等于k-1,问最少进行几次操作
【题解】
我们设dp[i]为前i项答案,得到方程dp[i]=min(dp[j]+S[i]-S[j]-aj*(i-j)),
dp[i]=S[i]+min(dp[j]-S[j]+aj*j-aj*i),为关于i的线性函数,
所以我们对f(y)=-ax*y+dp[x]-S[x]+ax*x进行斜率优化。
【代码】
#include <algorithm> #include <cstdio> #include <cstring> typedef long long LL; const int MAX_N=500010; int n,k; LL a[MAX_N],dp[MAX_N],S[MAX_N],deq[MAX_N]; LL f(int x,int y){return -a[x]*y+dp[x]-S[x]+a[x]*x;} bool check(int f1,int f2,int f3){ LL a1=-a[f1],b1=dp[f1]-S[f1]+a[f1]*f1; LL a2=-a[f2],b2=dp[f2]-S[f2]+a[f2]*f2; LL a3=-a[f3],b3=dp[f3]-S[f3]+a[f3]*f3; return (a2-a1)*(b3-b2)>=(b2-b1)*(a3-a2); } void solve(){ for(int i=0;i<n;i++)S[i+1]=S[i]+a[i]; int s=0,t=1; deq[0]=0; dp[0]=0; for(int i=k;i<=n;i++){ if(i-k>=k){ while(s+1<t&&check(deq[t-2],deq[t-1],i-k))t--; deq[t++]=i-k; }while(s+1<t&&f(deq[s],i)>=f(deq[s+1],i))s++; dp[i]=S[i]+f(deq[s],i); }printf("%lld\n",dp[n]); }int T; int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&k); for(int i=0;i<n;i++)scanf("%lld",&a[i]); solve(); }return 0; }
愿你出走半生,归来仍是少年