[LIS] CF1437E Make It Increasing
题目大意
给你一个数列 \(\{a_n\}\) ,一个集合 \(b\) , 对于每个 \(b\) 中的元素 \(x\), \(a_x\) 不能修改,其他都可以修改,问最少多少次可以将 \(a\) 修改为严格单调递增的。如果不存在,输出 \(−1\) 。\(a\) 中的所有元素在任意时刻必须都是整数。\((n\leq 5\times 10^5,1\leq a_i\leq 10^9)\)
题解
对于这一类涉及到单调递增的序列的题目,一般有两种套路:
-
第一种套路是对原序列做差分,那么如果原序列单调不降,那么差分后每个数都应该大于等于0;如果原序列单调上升,那么差分后每个数都应该大于0.比如Codeforces 1406D Three Sequences 这道题就可以从差分入手去考虑。
-
第二种套路是如果一个序列中的每个位置上的数都有不同的比较标准,我们就想让每个数去减去它自己的比较标准,从而统一所有数的比较标准。常见的模型是每个位置上的数关于这个位置上的下标有一个限制,那么我们把每个数减去它的下标再去考虑。还有就是我们可以通过这种方法把严格上升序列转化为非严格上升序列。比如严格上升序列 \(\{a_n\}\),它第 \(i\) 个位置上的数的比较标准就是要大于 \(i\),那么我们只要把每个 \(a_i\) 减去 \(i\),就可以得到统一的比较标准:只要所有的 \(a_i-i\geq 0\),并且所有 \(a_i-i\) 构成了一个非严格上升序列,这个序列就是严格上升的。
那么对于这道题,如果没有限制不能修改的位置,那么我们可以求得序列 \(\{a_n-n\}\) 的最长非严格上升子序列,然后只要修改在子序列之外的数即可,因为不要求严格上升,修改后的数可以与最长非严格上升子序列中的数相同,所以这样一定可以修改成功,且最优。但是这道题有不能修改的位置,我们只要在求最长非严格上升子序列的过程中,限制转移时不能修改的位置上的数一定要在最终得到的非严格上升子序列中即可。时间复杂度 \(O(n\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define RG register int
#define LL long long
template<typename elemType>
inline void Read(elemType &T){
elemType X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
T=(w?-X:X);
}
int a[500010],b[500010],ban[500010],B[500010];
int N,K;
int main(){
Read(N);Read(K);
for(RG i=1;i<=N;++i) Read(a[i]);
for(RG i=1;i<=K;++i){Read(b[i]);ban[b[i]]=1;}
for(RG i=2;i<=K;++i)
if(a[b[i-1]]-b[i-1]>a[b[i]]-b[i]){printf("-1\n");return 0;}
for(RG i=1;i<=N;++i)
a[i]-=i;
int top=0,Pre=0;
for(RG i=1;i<=N;++i){
if(!top || a[i]>=B[top]){
B[++top]=a[i];
if(ban[i]) Pre=top;
}
else{
int p=upper_bound(B+1,B+top+1,a[i])-B;
if(p<=Pre) continue;
//如果我们要更改的位置小于上一个不能修改的位置,那么就不能更改假想的答案序列
B[p]=a[i];
if(ban[i]) Pre=top=p;
//答案序列p后面的位置都不满足条件,因为它们不能大于这个不能修改的位置,因此对我们要的最长不下降子序列不能产生贡献
}
}
printf("%d\n",N-top);
return 0;
}