Codeforces 946G. Almost Increasing Array

Description

给定序列 \(A\),求至少改变几个位置的值可以使得 \(A\) 去掉一个元素之后就变成单增的
题面

Solution

朴素 \(DP\) 做法是,设 \(f[i]\) 表示前 \(i\) 个元素至少去掉的个数
\(f[i]=min(f[j]+j-i-1)\),\(a[i]-a[j]>=i-j\)
第二个条件其实就是 \(a[i]-i>=a[j]-j\)
那么就是老套路:把 \(a[i]\) 变成 \(a[i]-i\)
\(DP\)就变成了 \(f[i]=min(f[j]+j-i-1)\),\(a[i]>=a[j]\)
实际上就是 \(n-\) 最长不下降子序列

这题还可以多去掉一个元素,考虑怎么做:
实际上就是把去掉的元素之后的 \(a[i]\) 改为 \(a[i]-(i-1)\),也就是整体前移了一位

所以我们需要枚举一个删除的元素,考虑分治优化
我们发现这个\(DP\)是有传递性的,也就是说,用不同的 \(mid\) 更新的效果实际上是一样的,所以分治是可行的
我们把 \(mid\) 两边的序列按 \(a[i]\) 排序,然后单调指针扫描一下就可以了

#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int n,a[N],pre[N],suf[N],f[N],pos=0,ans=0;
inline int find(int x){
	int l=1,r=pos,mid,ret=0;
	while(l<=r){
		mid=(l+r)>>1;
		if(x>=f[mid])ret=mid,l=mid+1;
		else r=mid-1;
	}
	return ret;
}
struct node{
	int w,v;
	bool operator <(const node &p)const{return w<p.w;}
}p[N],q[N];
inline void solve(int l,int r){
	int mid=(l+r)>>1;
	if(l==mid || r==mid)return ;
	solve(l,mid);solve(mid,r);
	int L=0,R=0;
	for(int i=l;i<mid;i++)p[++L]=(node){a[i],pre[i]};
	for(int i=mid+1;i<=r;i++)q[++R]=(node){a[i]+1,suf[i]};
	sort(p+1,p+L+1);sort(q+1,q+R+1);
	for(int i=1,j=1,len=0;i<=R;i++){
		while(j<=L && p[j].w<=q[i].w)len=max(p[j].v,len),j++;
		ans=max(ans,len+q[i].v);
	}
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  scanf("%d",&n);f[0]=-N;
  for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]-=i;
  for(int i=1;i<=n;i++){
	  if(a[i]>=f[pos])f[++pos]=a[i],pre[i]=pos;
	  else{
		  int t=find(a[i]);
		  f[t+1]=min(f[t+1],a[i]);
		  pre[i]=t+1;
	  }
  }
  pos=0;
  for(int i=n;i>=1;i--){
	  if(-a[i]>=f[pos])f[++pos]=-a[i],suf[i]=pos;
	  else{
		  int t=find(-a[i]);
		  f[t+1]=min(f[t+1],-a[i]);
		  suf[i]=t+1;
	  }
  }
  solve(1,n);
  for(int i=1;i<=n;i++)ans=max(pre[i],ans),ans=max(suf[i],ans);
  cout<<max(n-ans-1,0)<<endl;
  return 0;
}

posted @ 2018-04-13 20:17  PIPIBoss  阅读(265)  评论(0)    收藏  举报