Codeforces Educational Codeforces Round 97 (Rated for Div. 2) E. Make It Increasing

题目链接

题解

在指定一些位置上的数不能更改的情况下需要将一个序列变为严格但调递增序列的所需要最少的修改次数

题解

  • 首先判断掉不能修改的情况
  • 不能修改的$m$个位置将序列划分为$m+1$个部分,显然这$m+1$个部分是互不影响的(因为边界已经确定了),那么可以考虑对于每一个部分分别考虑
  • 对于每一个部分我们需要找到最长的上升子序列使得该子序列的数都不需要更改且满足剩余的数一定能够通过修改使得该部分保持单调递增
  • 这个问题就是一个最长上升子序列的变形了,设位置$i$上的数为$a_i$,$dp_i$表示以第$i$个数结尾且满足上述条件的最长上升子序列的长度
  • 若$j$满足$j < i$并且$a_i - a_j$ >= $i - j$那么$dp_i$能由$dp_j$转移过来
  • 然后就可以进行$dp$了,因为数据范围较大所以可以用树状数组来加速$dp$,树状数组的下标存的是$a_i-i$的值,这个需要先离散化
  • 在对每一个部分处理完后记得初始化树状数组。
  • 查看代码
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 5e5+5;
    
    int a[maxn],b[maxn],c[maxn],tot,d[maxn];
    
    struct BIT
    {
        int bit[maxn];
        BIT(){memset(bit,-0x3f,sizeof(bit));}
        void modify(int n,int x,int val){
            for(int i = x;i <= n;i += i&-i)bit[i] = max(bit[i],val);
        }
        int query(int x){
            int ans = -1e9;
            for(int i = x;i;i -= i&-i)ans = max(ans,bit[i]);
            return ans;
        }
        void goback(int n,int x){
            for(int i = x;i <= n;i += i&-i)bit[i]=-1e9;
        }
    }bit;
    
    int solve(int l,int r)
    {
        int tl = l+1;
        int tr = r-1;
        if(tl>tr)return 0;
        int ans = 0;
        int v = lower_bound(c,c+tot,a[l]-l)-c+1;
        bit.modify(tot+1,v,0);
        for(int i = tl;i <= r;++i){
            int val = lower_bound(c,c+tot,a[i]-i)-c+1;
            int tmp = bit.query(val);
            bit.modify(tot+1,val,tmp+1);
            if(i==r)ans = max(0,tmp); 
        }
        for(int i = l;i <= r;++i){
            int val = lower_bound(c,c+tot,a[i]-i)-c+1;
            bit.goback(tot+1,val);
        }
        return (r-l-1-ans);
    }
    
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        a[0] = -1e9;
        a[n+1] = 2e9;
        for(int i = 1;i <= n;++i)scanf("%d",&a[i]),c[i]=a[i]-i;
        c[n+1] = a[n+1]-n-1;
        c[0] = -1e9;
        sort(c,c+2+n);
        tot = unique(c,c+2+n)-c;
        for(int i = 1;i <= m;++i)scanf("%d",&b[i]);
        for(int i = 2;i <= m;++i){
            if(a[b[i]]-a[b[i-1]]<b[i]-b[i-1]){
                printf("-1\n");
                return 0;
            }
        }
        int ans = 0;
        int last = 0;
        for(int i = 1;i <= m;++i){
            ans += solve(last,b[i]);
            last = b[i];
        }
        ans += solve(last,n+1);
        cout<<ans<<endl;
        return 0;
    }
    
posted @ 2020-10-28 21:12  tryatry  阅读(123)  评论(0编辑  收藏  举报