CF1437E Make It Increasing 题解

题目大意

给你一个数列 (a) ,一个集合 (b) , 对于每个(b) 中的元素(x)(a_x) 不能修改,其他都可以修改,问最少多少次可以将(a) 修改为严格单调递增的。如果不存在,输出 (-1)

思路

我们先考虑不存在的情况,一定是存在(x,yin b,x < y) ,(a_y-a_x < y-x) , 这样无法在 (a_x,a_y) 中任意修改满足严格单调递增。


那么接下来考虑最小的修改次数。

加入我们没有修改限制呢?

那么就是一个经典的(DP) 问题了。

我们将原数组变形,让(a_x = a_x -x),然后跑一遍最长不下降子序列就行了(用 (nlogn) 的算法),答案就是 (n-)最长不下降子序列长度了。

但是有限制,我们先考虑一下没有限制的代码。

for(int i = 1;i <= n;i++) a[i] -= i;
for(int i = 1;i <= n;i++){
	if(e == 0 || a[i] >= l[e]) {
	    l[++e] = a[i];
	}
	else{
	    int p = upper_bound(l+1,l+e+1,a[i])-l;
	    l[p] = a[i];
	}
}//e : l 数组的末尾标号。

这样的代码会在遍历 (a) 数组时不断更新自己的一个假想的答案序列,使它每个值尽可能的小,然后容下一个新的值,使答案最大。

在判过是否满足后,(forall xin b)(a_x) 一定也可以在处理后形成一个不下降子序列,我们要保证不修改它们,就要让它们处于这个假想的答案序列中,于是我们可以加上几行:

//lasb ,上一个不能修改的位置在我们当前维护的假想答案序列中的位置。
//ban[i] , a[i] 是否禁止修改。
for(int i = 1;i <= n;i++){
	if(e == 0 || a[i] >= l[e]) {
	    l[++e] = a[i];
	    if(ban[i]) lasb = e;
	}
	else{
	    int p = upper_bound(l+1,l+e+1,a[i])-l;
	    if(p <= lasb) continue;//如果我们要更改的位置小于上一个不能修改的位置,那么就不能更改假想的答案序列
	    l[p] = a[i];
	    if(ban[i]) lasb = p,e = p;//答案序列p后面的位置都不满足条件,因为它们不能大于这个不能修改的位置,因此对我们要的最长不下降子序列不能产生贡献。
	}
}

魔改一下这个算法后,就可以(AC) 了。(具体理解见代码,可以结合一下(nlogn)的最长不下降子序列的算法)

Code

#define re(x) read(x)

const int MAXN = 1e6+10;

int n,k;
int a[MAXN],l[MAXN],e,b[MAXN],lasb=0;
bool ban[MAXN];

int main (){
    re(n);re(k);
    for(int i = 1;i <= n;i++) re(a[i]);
    for(int i = 1;i <= k;i++) re(b[i]),ban[b[i]]=1;
    sort(b+1,b+k+1);
    for(int i = 2;i <= k;i++)
		if(a[b[i-1]] - b[i-1] > a[b[i]] - b[i]){
	    	return puts("-1"),0;
		}
    for(int i = 1;i <= n;i++) a[i] -= i;
    for(int i = 1;i <= n;i++){
		if(e == 0 || a[i] >= l[e]) {
	    	l[++e] = a[i];
	    	if(ban[i]) lasb = e;
		}
		else{
	    	int p = upper_bound(l+1,l+e+1,a[i])-l;
	    	if(p <= lasb) continue;
	    	l[p] = a[i];
	    	if(ban[i]) lasb = p,e = p;
		}
    }
    printf("%d",n-e);
    return 0;
}
原文地址:https://www.cnblogs.com/werner-yin/p/-solution-CF-1437E.html