P2501 [HAOI2006] 数字序列
/* a1...an (n<=3.5e4)问:将其变成严格上升序列 1.最少改变多少个数2.在1情况下 改变值的和的 最小值 1.对任意满足要求·的序列 ai-aj>=i-j ->ai-i>=aj-j ->求最长不降子序列长度 2. 1.合法序列可能不止 一个 2.bi bj间全是不合法的数 考虑 某一个区间 bi...bj 不减 但若某一片段 上升点数!=下降数目 就可以调整到两边 综合效益不减 反复 则 bi==...b_k , b_k+1==...bj 枚举即可 ->在dp过程中 如何存储 最长的不降序列 f[i]以i结尾的 LIS 长度 minof[i] 长度为i的 LIS 的最小结尾 ed[i] 长度为i的 LIS 结尾数字 g[i] 最后一位是b[i]的LIS 代价 :::循环中 1-n+1 边界应该处理好 */ /* */ #include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<string.h> #include<queue> #include<vector> #include<bits/stdc++.h> typedef long long ll; #define ddd printf("-----------------------\n"); using namespace std; const int maxn=4e4 +10; const int mod=998244353; const int inf=0x3f3f3f3f; int n,a[maxn],b[maxn],f[maxn],minof[maxn],len; ll g[maxn],pre[maxn],suf[maxn]; vector<int> ed[maxn]; int main() { ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) {cin>>a[i];b[i]=a[i]-i;} b[n+1]=inf,b[0]=-inf; for(int i=1;i<=n+1;i++){ int l=0,r=len; while(l<r) { int mid=(l+r+1)>>1; if(minof[mid]<=b[i]) l=mid;//!!!!!!!!!!!!!!!! <b[i]的最大值 包括 坐标最靠右 else r=mid-1; } if(l==len) len++; minof[l+1]=b[i]; f[i]=l+1; ed[f[i]].push_back(i); } memset(g,inf,sizeof(g)); g[0]=0;ed[0].push_back(0);//0-1-n-n+1 for(int i=1;i<=n+1;i++) { for(int j=0;j<ed[f[i]-1].size();j++) { int from=ed[f[i]-1][j]; if(from>i||b[from]>b[i]) continue; pre[from]=suf[i]=0; for(int k=from+1;k<=i-1;k++) pre[k]=pre[k-1]+abs(b[from]-b[k]); for(int k=i-1;k>=from+1;k--) suf[k]=suf[k+1]+abs(b[i]-b[k]); for(int k=from;k+1<=i;k++) g[i]=min(g[i],g[from]+pre[k]+suf[k+1]); } } cout<<n+1-len<<'\n'<<g[n+1]; return 0; }