题目链接:https://codeforces.com/problemset/problem/1437/E
分析
简单地说,就是求被几个确定的数分成的每个区间内的最长上升子序列,我们可以对每个区间求一遍最长上升子序列。
题目中会有约束:区间的两边是已经确定的数。可以先去除已经在这两个数范围之外的数,因为这样的数是一定要进行操作来修改的。
那么如何求最长上升子序列呢,可以参考文章,时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
但是要将一个数列修改成严格单调是不能仅仅看最长上升子序列的,例如
[
3
,
5
,
4
,
6
]
[3,5,4,6]
[3,5,4,6],最长上升为
[
3
,
4
,
6
]
[3,4,6]
[3,4,6],但是不能仅仅通过修改
5
5
5来达到要求。也就是对于
1
⩽
i
<
j
⩽
n
1\leqslant i<j \leqslant n
1⩽i<j⩽n,要满足
a
i
<
a
j
a_i < a_j
ai<aj以及
j
−
i
−
1
⩽
a
j
−
a
i
−
1
j-i-1 \leqslant a_j-a_i-1
j−i−1⩽aj−ai−1,于是不等式就变成了
a
i
−
i
⩽
a
j
−
j
a_i-i\leqslant a_j-j
ai−i⩽aj−j,所以我们只要到
a
k
−
k
a_k-k
ak−k的最长非递减子序列即可,具体可以参考文章。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define fi first
#define se second
#define pb push_back
#define MP make_pair
const int inf = 1e9 + 10;
const int N = 5e5 + 10;
int n,m;
int a[N], b[N];
vector<int> v;
int cal(vector<int> x)
{
vector<int> tmp;
tmp.pb(x[0]);
for(int i=1;i<x.size();i++)
{
if(tmp[tmp.size()-1] <= x[i])
{
tmp.pb(x[i]);
continue;
}
int pos = upper_bound(tmp.begin(), tmp.end(), x[i]) - tmp.begin();
tmp[pos] = x[i];
}
return tmp.size();
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]), a[i] -= i;
for(int i=1;i<=m;i++) scanf("%d",&b[i]);
b[m + 1] = n + 1;
a[0] = -inf, a[n + 1] = inf;
int flag = 1;
int ans = 0;
for(int i=0;i<=m;i++)
{
int l = b[i], r = b[i + 1];
if(a[l] > a[r])
{
flag = 0;
break;
}
if(l + 1 == r) continue;
v.clear();
for(int i=l+1;i<r;i++) if(a[i] >= a[l] && a[i] <= a[r]) v.pb(a[i]);
ans += r - l - 1;
if(v.size()) ans -= cal(v);
}
if(flag == 0) printf("-1\n");
else printf("%d\n",ans);
return 0;
}