题目大意:
给你一个序列:
{
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
n≤5e5,0≤k≤n
题目思路:
首先,前置知识点: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]的最大位置.这个长度即为答案。
代码:略.