題目大意
給你一個數列 \(a\) ,一個集合 \(b\) , 對於每個\(b\) 中的元素\(x\), \(a_x\) 不能修改,其他都可以修改,問最少多少次可以將\(a\) 修改為嚴格單調遞增的。如果不存在,輸出 \(-1\) 。
思路
我們先考慮不存在的情況,一定是存在\(x,y\in 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 x\in 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;
}