题意:
有一个长度为n的数组a。你可以删除一个位置之后进行操作,一次操作可以把任意位置上的数字变成任意的值,问最少需要多少操作能使得数列变成严格上升的。
n<=200000
分析:
如果没有删除,那是个经典问题,我们只要对{ai-i}求最长不降子序列就行了
现在有个删除,若删除一个元素,那么它后面那些元素的位权-1,所以对于每个位置我们关心的只是它的位权是i还是i-1
于是考虑dp,dp[0][i][j]表示做完了前i个位置,之前没有删除元素,长度为j的不降子序列最后一位的最小值,dp[1][i][j]同理
考虑转移,dp[0][i]=(a[i]-i)二分插入dp[0][i-1],dp[1][i]的每个位置=min((a[i]-i+1)二分插入dp[1][i-1], dp[0][i-1])
其中dp[1][i][j]=min(dp[1][i][j],dp[0][i-1][j])这个转移很不好,是O(n)的,其它转移都是O(logn)的是可以接受的
仔细观察发现,实际上只有上一次的a[i-1]-(i-1)插入的位置可能会更新此时的dp[1][i],所以这个转移其实可以做到O(1)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=2e5,inf=2e9; 4 int dp[2][maxn+5]; 5 int a[maxn+5]; 6 int n,len0,len1; 7 int main() 8 { 9 scanf("%d",&n); 10 for(int i=1;i<=n;++i) scanf("%d",&a[i]); 11 for(int i=0;i<=n;++i) dp[0][i]=dp[1][i]=inf; 12 int last=1; 13 dp[0][1]=a[1]-1,len0=1; 14 for(int i=2;i<=n;++i) 15 { 16 int p=upper_bound(dp[1]+1,dp[1]+len1+1,a[i]-i+1)-dp[1]; 17 dp[1][p]=a[i]-i+1; 18 len1=max(len1,p); 19 20 dp[1][last]=min(dp[0][last],dp[1][last]); 21 len1=max(len1,last); 22 23 p=upper_bound(dp[0]+1,dp[0]+len0+1,a[i]-i)-dp[0]; 24 dp[0][p]=a[i]-i; 25 last=p; 26 len0=max(len0,p); 27 } 28 printf("%d\n",min(n-len0,n-len1-1)); 29 return 0; 30 }