acm-(LIS、序列)Educational Codeforces Round 97 (Rated for Div. 2) E. Make It Increasing

1 篇文章 0 订阅

题面
传送门
本题就是给你一个数组 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]xa[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); 
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值