1 条题解
-
0
注意到最终的序列一定是若干形如 的连续子序列拼接起来的,我们称这样的连续子序列为一个连续段。
为了方便优化,我们把问题变为至多保留多少个数的问题。设这个问题答案为 ,则原问题的答案为 。
考虑 DP。设 为将原数列 区间调整为“好”的,且 位置为一个连续段的末尾,至多保留多少数, 为 。
显然有转移:
$$f_i=\max_{0 \le j < i}\{f_j + \sum_{k=j+1}^{i} [a_k=k-j-1]\}$$这表示枚举 ,计算将 作为一个连续段时的答案,时间复杂度 。
为了方便,令 。转移方程为 。考虑优化 的计算,对于一个 ,通过上面式子发现他只会对 产生 的贡献。前提是 。
这样有一个 的做法:在计算 前,将 加 ,计算 ,令 。
用线段树可以优化到 ,但是没必要,也无法通过所有测试点。
注意到 在计算过程中是单调不降的,可以维护一个变量 。
具体为在将 加 后,令 。计算 时令 即可。
时间复杂度为 。代码难度几乎没有。
其实有一个另一个 的做法:设 为考虑到 , 时最多保留多少个数,对正解没有启发,故略去。
#include<bits/stdc++.h> #define N 10010000 using namespace std; int a[N],f[N],g[N],n,mx; inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar(); return x*f; } int main(){ n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++){ a[i]=i-a[i]-1; if(a[i]>=0) ++g[a[i]],mx=max(mx,g[a[i]]); g[i]=f[i]=mx; } return cout<<n-f[n],0; }
设 为 时确定 的最小代价,可以证明只有 的转移点 可能最优,变形得到 ,由同一个 可以唯一确定 可知时间复杂度为 。
- 1
信息
- ID
- 311
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 5
- 已通过
- 2
- 上传者