• CF1437E Make It Increasing


    题意描述:

    洛谷

    给你一个长度为 (n) 的序列 (a), 和一个大小为 (m) 的集合 (b), 其中 (b) 集合中的位置不能改,问你最少要修改多少次使得 序列 (a) 严格递增。,修改后保证 (a) 为整数。 无解输出 (-1).

    数据范围: (n,mleq 5 imes 10^5, a_ileq 10^9)

    solution

    比较好玩的构造题。

    有一个结论:如果 (i,j) ((i<j)) 都不修改, 则需要满足 (a_j-a[i] geq j-i)

    因为你要保证 (i-j) 这一段区间严格单调递增,因此每一项至少要与前一项差 (1), 那么可以推出 (a_j) 至少要和 (a_i)(j-i)(1) 才能符合条件,转化成数学形式即 (a_j - a_igeq j-i)

    有了这个结论我们就很好做了。

    首先,我们先考虑一下无解的情况,如果 (i,jin b, i < j) 且 $a[j]-a[i] < j-i $ ,那么肯定是无解的。

    接下来考虑怎么求最少的修改次数。

    显然 (b) 集合会把序列 (a) 分成 (m+1) 段,我们对每一段求一个最少的修改次数,在相加就是最后的答案。

    我们想让修改次数最少,所以这一段区间中的数尽可能多的不被修改。

    由结论可得如果 (a_i)(a_j) 都不被修改,则要满足 (a_j-a_i geq j-i) ,即: (a_j-j geq a_i-i)

    我们把序列 (a) 中每个元素的价值设为 (a_i-i), 然后对 ((b[i-1],b[i])) 这一段区间求一个最长上升子序列的长度就是最多可以保留的数的个数。

    但由于 (b[i],b[i-1]) 这两个位置不能被修改即必须被选入最长上升子序列中,我们需要对代码魔改一下。

    • 初值只有 (f[b[i-1]] = 1), 这样每个元素都可以由 (f[b[i-1]]) 转移过来,也就等价于 (b[i-1]) 一定会被选上。

    • (b[i]) 被选上也就是最长上升子序列的长度为 (f[b[i]])

    求最长上升子序列用树状数组优化一下即可。

    坑点:树状数组的初值要设为 (-inf)

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N = 5e5+10;
    const int inf = 1e9+10;
    int n,m,ans,flag,cnt;
    int f[N],a[N],b[N],w[N],tr[N];
    inline int read()
    {
        int s = 0, w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int lowbit(int x){return x & -x;}
    void cover(int x,int val)//还原树状数组
    {
    	for(; x <= N-5; x += lowbit(x)) tr[x] = val;
    }
    void chenge(int x,int val)
    {
        for(; x <= N-5; x += lowbit(x)) tr[x] = max(tr[x],val);
    }
    int query(int x)
    {
        int res = -inf;
        for(; x; x -= lowbit(x)) res = max(res,tr[x]);
        return res;
    }
    int main()
    {
        n = read(); m = read(); 
        w[0] = -inf; w[n+1] = inf;
        b[++cnt] = w[0]; b[++cnt] = w[n+1];
        for(int i = 1; i <= n; i++) b[++cnt] = w[i] = read() - i;
        sort(b+1,b+cnt+1);
        int num = unique(b+1,b+cnt+1)-b-1;
        for(int i = 0; i <= n+1; i++) w[i] = lower_bound(b+1,b+num+1,w[i])-b;
        for(int i = 1; i <= m; i++)
        {
            b[i] = read();
            if(w[b[i]] < w[b[i-1]]) flag = 1;
        }
        b[0] = 0; b[m+1] = n+1;
        memset(tr,128,sizeof(tr));
        if(flag) printf("%d
    ",-1);
        else
        {
            for(int i = 1; i <= m+1; i++)//对每一段进行处理
            {
                f[b[i-1]] = 1;
                chenge(w[b[i-1]],1);
                for(int j = b[i-1]+1; j <= b[i]; j++)
                {
                    f[j] = query(w[j]) + 1;
                    chenge(w[j],f[j]);
                }
                for(int j = b[i-1]; j <= b[i]; j++) cover(w[j],-inf); 
                ans += b[i] - b[i-1] + 1 - f[b[i]];
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    
    
  • 相关阅读:
    “约瑟夫问题”实现代码
    “百钱买百鸡”问题
    链栈的表示与实现
    个人作品Demo3PLY文件的读取
    系统程序员成长计划容器与算法(一)(下)
    循环单链表的建立
    链式队列元素删除实现
    使用链栈实现数制的转换
    系统程序员成长计划容器与算法(二)(上)
    个人作品Demo4STL文件读取
  • 原文地址:https://www.cnblogs.com/genshy/p/14435198.html
Copyright © 2020-2023  润新知