传送门
本题就是给你一个数组
a
[
n
]
a[n]
a[n],然后其中有
k
k
k个位置不能修改,其它位置可修改为任意数值,问最少修改几次能够让整个序列严格单调递增。
考虑两个相邻的不能修改的位置 l , r l,r l,r,也就是说我们要让 a [ l ] , a [ l + 1 ] , a [ l + 2 ] , . . . , a [ r ] a[l],a[l+1],a[l+2],...,a[r] a[l],a[l+1],a[l+2],...,a[r]单调递增,并且 a [ l ] , a [ r ] a[l],a[r] a[l],a[r]不能被修改。
先复习一下最长单调不降序列的
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)做法,我们设置一个数组
d
p
[
l
e
n
]
dp[len]
dp[len]表示长度为
l
e
n
len
len的单调不降序列的最后一个元素的最小值。现在由小到大遍历原数组,然后分两种情况讨论,若当前元素
x
x
x大于
d
p
[
l
e
n
]
dp[len]
dp[len],那么我们
d
p
[
+
+
l
e
n
]
=
x
dp[++len]=x
dp[++len]=x;否则我们二分查找第一个大于
x
x
x的元素,假设它的位置是
i
i
i,说明长度为
i
i
i的单调不降序列的末尾元素的最小值可以被替换为
x
x
x,因此有
d
p
[
i
]
=
x
dp[i]=x
dp[i]=x。
最终
l
e
n
len
len就是最长单调不降序列的长度。
然后本题要求 ∀ x , y ∈ [ 1 , n ] , x ≠ y \forall x,y\in[1,n],x\ne y ∀x,y∈[1,n],x=y有 a [ x ] < a [ y ] ⇒ a [ x ] − x ≤ a [ y ] − y a[x]<a[y]\Rightarrow a[x]-x\le a[y]-y a[x]<a[y]⇒a[x]−x≤a[y]−y因此我们不妨给所有元素减去一个它对应的位置,这样原问题转化为保证所有元素单调不降。对于每个相邻的不可修改位置我们求一次答案即可。
于是对于减去下标后的 a [ l ] , a [ l + 1 ] , a [ l + 2 ] , . . . , a [ r ] a[l],a[l+1],a[l+2],...,a[r] a[l],a[l+1],a[l+2],...,a[r]而言,我们需要求出包含 a [ l ] , a [ r ] a[l],a[r] a[l],a[r]的最长单调不降序列长度。考虑刚才的 d p dp dp过程,我们需要保证 d p [ 1 ] = a [ l ] dp[1]=a[l] dp[1]=a[l],为什么呢,因为如果 d p [ 1 ] dp[1] dp[1]在后面遍历 a a a数组的过程中被修改了的话,那么后面产生的最长单调不降序列可能就不是以 a [ l ] a[l] a[l]开头了,不符合条件,相当于 a [ l ] a[l] a[l]是整个序列中最小的元素,自然的根据定义有 d p [ 1 ] = a [ l ] dp[1]=a[l] dp[1]=a[l]。然后我们最后不取 l e n len len,而是找到 a [ r ] a[r] a[r]加入 d p dp dp的位置,这代表的就是以 a [ l ] a[l] a[l]为起点, a [ r ] a[r] a[r]为终点的最长单调不降子序列长度。答案就是数组总长度减去最长单调不降子序列长度,由于是分段求解,我们要将所有的这些答案求和得到最终答案。
其实我们也可以不用分段去求解答案,直接对整个数组求带限制最长单调不降子序列,不过复杂度大一些,因为每次二分的对象都是全数组。
这里给出一份参考代码,是分段求答案的方法:
int a[maxn],g[maxn],h[maxn];
inline int solve(int l,int r){
register int ans=0,tot=0,len=0,x=1;
if(l+1==r)return 0;
FOR(i,l,r+1)tot++,g[tot]=a[i]-tot;
h[++len]=g[1];
FOR(i,2,tot+1){
x=upper_bound(h+1,h+1+len,g[i])-h;
if(x==1)continue;
h[x]=g[i];
len=max(x,len);
}
return tot-x;
}
int main(){
register int n=rd(),k=rd();
FOR(i,1,n+1)a[i]=rd();
register int failed=false,pre=0,cur=0,ans=0;
a[0]=-2e9;
a[n+1]=2e9;
FOR(i,0,k){
cur=rd();
if(pre && a[cur]-a[pre]<cur-pre)failed=true;
if(!failed){
if(!pre){
ans+=solve(0,cur);
}else ans+=solve(pre,cur);
}
pre=cur;
}
if(failed)return wrn(-1),0;
ans+=solve(cur,n+1);
wrn(ans);
}