【2020NOI.AC省选模拟#5】C. 光滑序列
题目链接
原题解:
光滑的序列一定有长度为K的循环节。
使用动态规划,设F(i,j)为使前i个整数的和为j的最小修改次数。
记cost(i,v)为令Ai,Ai+K,Ai+2K,⋯等于v需要而修改次数。则F(i,j)=min。
可以发现,A_i,A_{i+K},A_{i+2K},\cdots中至多有O(\dfrac{N}{K})个不同的值。
我们可以先用F(i,j)=\min \limits_v \{F(i-1,j-v)+\left\lfloor \dfrac{N-i}{K} \right\rfloor +1\}一次性转移没有在这些位置中出现过的v,然后再对这O(\dfrac{N}{K})个v单独转移。
复杂度为O(KS\times \dfrac{N}{K})=O(NS)。
补充:
O(N^2)比O(N^3)改进的地方是两点:
- 使用f(i-1,j)的前缀最小值来设定f(i,j)的初值。
- 枚举增加的数进行转移,于是可以判断这种数存不存在,进而减少无效转移。因为模K相同的位置只有O(\dfrac{N}{K})个,所以复杂度就对了。
代码(100分):

#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define IL inline #define RG register using namespace std; #define RI RG int #define RC RG char const int N=5000; int n,k,s,a[N+3]; int b[N+3][N+3],c[N+3]; int f[N+3][N+3]; int main(){ scanf("%d%d%d",&n,&k,&s); for(RI i=1;i<=n;i++) scanf("%d",&a[i]); for(RI i=1,t=1;i<=n;i++,t+1>k?t=1:t++){ b[t][a[i]]++; c[t]++; } for(RI i=1;i<=s;i++) f[0][i]=n+1; f[0][0]=0; for(RI i=1;i<=k;i++){ for(RI j=0,x=f[i-1][0];j<=s;j++){ x=min(x,f[i-1][j]); f[i][j]=x+c[i]; } for(RI j=0;j<=s;j++) if(b[i][j]) for(RI p=j;p<=s;p++) f[i][p]=min(f[i][p],f[i-1][p-j]+c[i]-b[i][j]); } printf("%d",f[k][s]); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步