题解-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;
}
祝大家学习愉快!