Loading

题解-CF1437E Make It Increasing

题面

CF1437E Make It Increasing

\(n\) 个数 \(a_i\),固定 \(k\) 个下标 \(b_i\),求只修改不在 \(b_i\) 中的下标的值使 \(a_i\) 严格单调递增的最少修改次数。

数据范围:\(1\le n\le 5\cdot 10^5\)\(0\le k\le n\)


题解

分成 \(k+1\) 段做没有问题,蒟蒻的做法是线段树维护 dp

旁边老爷的做法是区间长度减去最长上升子序列长度,蒟蒻没想到,还想了单调队列优化好久。

题目转化为对于一个区间,第一个元素最后一个元素固定的最少修改次数。

\(a_i\) 减去 \(i\),就变成非严格单调递增了。

\(f_i\) 表示不修改 \(i\) 的前缀最少修改次数。

\[f_i=\min_{j=1,a_j\le a_i}^{i-1} (f_j+i-j-1)\Longrightarrow f_i=i-1+\min_{j=1,a_j\le a_i}^{i-1}( f_j-j) \]

所以可以建一个 \(a_i\) 值域线段树,值为 \(f_i-i\)

\(f_i\) 的值就是当前线段树 \([0,a_i]\) 之间的最大值 \(+i-1\)

每次求出 \(f_i\) 后在 \(a_i\) 上更新 \(f_i-i\) 即可。


代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair((a),(b))
#define x first
#define y second
#define bg begin()
#define ed end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
#define R(i,a,b) for(int i=(a),i##E=(b);i<i##E;i++)
#define L(i,a,b) for(int i=(b)-1,i##E=(a)-1;i>i##E;i--)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=5e5+2;
int n,a[N],dn,d[N],k,b[N],ans;

//SegmentTree
const int tN=N<<2;
#define mid ((l+r)>>1)
int mn[tN];
void build(int k=0,int l=0,int r=dn){
    mn[k]=iinf; if(r-l==1) return;
    build(k*2+1,l,mid),build(k*2+2,mid,r);
}
void pushup(int k){mn[k]=min(mn[k*2+1],mn[k*2+2]);}
void fixmn(int x,int v,int k=0,int l=0,int r=dn){
    if(r<=x||x+1<=l) return;
    if(r-l==1) return mn[k]=min(mn[k],v),void();
    fixmn(x,v,k*2+1,l,mid),fixmn(x,v,k*2+2,mid,r),pushup(k);
}
int rangemn(int x,int y,int k=0,int l=0,int r=dn){
    if(r<=x||y<=l) return iinf; if(x<=l&&r<=y) return mn[k];
    return min(rangemn(x,y,k*2+1,l,mid),rangemn(x,y,k*2+2,mid,r));
}

//DP
int tmp[N];
int dp(int* arr,int len){
    R(i,dn=0,len) d[dn++]=arr[i];
    sort(d,d+dn),dn=unique(d,d+dn)-d;
    R(i,0,len) tmp[i]=lower_bound(d,d+dn,arr[i])-d;
    build(),fixmn(tmp[0],0); int res=-1;
    R(i,1,len){
        res=rangemn(0,tmp[i]+1)+i-1;
        fixmn(tmp[i],res-i);
    }
    // R(i,0,len) cout<<arr[i]<<' ';cout<<'\n';
    // R(i,0,len) cout<<f[i]<<' ';cout<<'\n';
    // cout<<f[len-1]<<'\n';
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>k,a[0]=-iinf,a[n+1]=+iinf;
    R(i,1,n+1) cin>>a[i],a[i]-=i;
    R(i,0,k) cin>>b[i];
    R(i,1,k)if(a[b[i]]<a[b[i-1]]) cout<<-1<<'\n',exit(0);
    if(k==0) ans=dp(a,n+2);
    else {
        ans+=dp(a,b[0]+1);
        R(i,1,k) ans+=dp(a+b[i-1],b[i]-b[i-1]+1); 
        ans+=dp(a+b[k-1],n-b[k-1]+2);
    }
    cout<<ans<<'\n';
    return 0;
}

祝大家学习愉快!

posted @ 2020-10-28 15:55  George1123  阅读(282)  评论(1编辑  收藏  举报