简直神题
直接放别人博客https://www.cnblogs.com/iwtwiioi/p/4160945.html
但我的写法不太一样
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define maxn 35005
#define LL long long
using namespace std;
int n,a[maxn],f[maxn],cnt,head[maxn],mx,mn[maxn];
LL g[maxn],s1[maxn],s2[maxn];
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
struct EDGE{
int to,nxt;
}edge[maxn];
inline void add(int x,int y){
edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}
inline int find(int x){
int l=1,r=mx,t=0;
while(l<=r){
int mid=(l+r)>>1;
if(mn[mid]<=x) l=mid+1,t=mid;
else r=mid-1;
}
return t;
}
inline void dp(){//先解决第一问,求出f数组
memset(mn,0x3f,sizeof mn);
mn[0]=-1<<30;
for(int i=1;i<=n;i++){
int tmp=find(a[i]);
f[i]=tmp+1;
mx=max(mx,tmp+1);
mn[tmp+1]=min(mn[tmp+1],a[i]);
}
}
inline void solve(){
for(int i=n;i>=0;i--) add(f[i],i),g[i]=1LL<<60;//向可转移的点连边
g[0]=0,a[0]=-1<<30;
for(int x=1;x<=n;x++){
for(int i=head[f[x]-1];i;i=edge[i].nxt){
int p=edge[i].to;
if(p>x) break;
if(a[p]>a[x]) continue;
for(int j=p;j<=x;j++)
s1[j]=abs(a[p]-a[j]),s2[j]=abs(a[x]-a[j]);
for(int j=p+1;j<=x;j++)//前缀和
s1[j]+=s1[j-1],s2[j]+=s2[j-1];
for(int j=p;j<x;j++)
g[x]=min(g[x],g[p]+s1[j]-s1[p]+s2[x]-s2[j]);//枚举最优的点
}
}
}
int main(){
n=rd(); mx=-1e9;
for(int i=1;i<=n;i++)
a[i]=rd()-i;
a[++n]=1<<30;
dp();
solve();
printf("%d\n%lld\n",n-f[n],g[n]);
return 0;
}