Educational Round 97-E:魔改LIS贪心算法

题目大意:

给你一个序列: { a 1 , . . . , a n } \{a_1,...,a_n\} {a1,...,an}.固定 k k k个位置不允许改动.问你最小改变次数使得该序列变成严格上升序列.
n ≤ 5 e 5 , 0 ≤ k ≤ n n\leq5e5,0 \leq k \leq n n5e5,0kn

题目思路:

首先,前置知识点:hdu 5256

我们知道,求非严格LIS的最小代价为 n - len(非严格LIS).

但是严格LIS的最小代价不能直接这么算,因为:例如:

1 2 3 3 4. 最长上升子序列为: 1 2 3 4.但是最少改2次.

b [ i ] = a [ i ] − i b[i]=a[i]-i b[i]=a[i]i.对 b b b求非严格LIS即为答案.

方法一:

考虑计算dp数组时,实际上是不断更新一个假想数组

我们要做的就是强制让 固定的数组 最后出现在假想数组 中.

那么我们维护一个lastb,代表 固定的数组 在 假想数组中最后出现的位置(注意,这个过程中,lastb一定是递增的),

具体细节可见代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
const int maxn = 5e5 + 5;
const int mod = 1e9 + 7;
int a[maxn] , dp[maxn] , b[maxn] , cnt;
bool bk[maxn];
int main()
{
    ios::sync_with_stdio(false);
    int n , k; cin >> n >> k;
    for (int i = 1 ; i <= n ; i++){
        cin >> a[i]; a[i] -= i;
    }
    bool ok = true;
    a[0] = -1e9;
    for (int i = 1 ; i <= k ; i++){
        cin >> b[i];
        if (a[b[i]] < a[b[i - 1]]) {
            ok = false;
            break;
        }
        bk[b[i]] = true;
    }
    if (!ok){
        cout << "-1" << endl;
        return 0;
    }
    int lastb = 0;
    for (int i = 1 ; i <= n ; i++){
        if (cnt == 0 || a[i] >= dp[cnt]){
            dp[++cnt] = a[i];
            if (bk[i]) lastb = cnt; // 更新lastb
        }
        else {
            int pos = upper_bound(dp + 1 , dp + 1 + cnt , a[i]) - dp;
            if (pos <= lastb) continue; // 如果一个数出现在lastb之前了,代表这个数一定要改.
            // 例如 1 '3' 2.   2一定要改.
            dp[pos] = a[i];
            if (bk[i]) lastb = pos , cnt = pos; // 更新lastb , 并且pos之后的所有数一定都要改.
        }
    }
    cout << n - cnt << endl;
    return 0;
}
方法二:

考虑 这 k k k个固定的数将整个序列划分成 k + 1 k+1 k+1个段.每一个段互不干扰.那么我们就是求每个段的非严格LIS最小代价即可.但是相邻两段之间,首尾的值已经内定了.那么就是 确定首尾的 非严格LIS.

解决方法是:对于区间 [ l , r ] [l,r] [l,r]先内定 d p [ 1 ] = a [ l ] dp[1] = a[l] dp[1]=a[l].然后正常求 [ l + 1 , r ] [l+1,r] [l+1,r].

然后最后找小于等于 a [ r ] a[r] a[r]的最大位置.这个长度即为答案。

代码:略.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值