codeforces 946G

题意:

  有一个长度为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 }
View Code

 

  

posted @ 2018-03-22 11:43  Chellyutaha  阅读(261)  评论(0编辑  收藏  举报