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;
}

 

posted @ 2023-12-23 11:12  JMXZ  阅读(8)  评论(0)    收藏  举报