zoukankan      html  css  js  c++  java
  • 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
    ",min(n-len0,n-len1-1));
    29     return 0;
    30 }
    View Code

      

  • 相关阅读:
    只要肯下功夫,十岁也能学得会的 Docker 精简版!
    sprintf和sscanf的用法
    ubuntu在线安装vscode
    Makefile模板
    本地Git配置绑定远程Github账户
    mysql多表查询
    VS error 2019 错误
    Oracle--约束
    Oracle--增删查
    Oracle--子查询
  • 原文地址:https://www.cnblogs.com/wmrv587/p/8622632.html
Copyright © 2011-2022 走看看