CF EDU 97 E - Make It Increasing
LIS
题意
给定数组 $a $, (n <= 5e5), 有一个集合 b ,b 里面存的是 a 数组的某些下标,这些位置的 a 的值不能改变
其余位置可花 1 代价变为任意一个整数,求让 a 变成严格单调递增的最小代价;若不可以则输出 -1
思路
有个套路是若让数组是严格单调递增的,可以令 \(a_i=a_i-i\), 之后把 \(a\) 变成非严格单调递增就可满足原来的 a 是严格单调递增的
这样就可以把数组分为若干区间,每个区间的两端是不能改变的位置,设两端下标为 l, r
- 若 \(a[l] >a[r]\), 则可直接输出 -1
- 若\(a[l]<=a[r]\), 则把 [l+1, r - 1] 这些位置的数,在 \([a[l], a[r]]\) 范围内的数求一个 LIS,就是最多的不用改变的数,再求每个区间的最小代价
有个小技巧是可以令 a[0] = -INF, a[n+1] = INF, 分别作为第一个区间的左端点,最后一个区间的右端点,这样可以不用特判
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
#define endl "\n"
typedef long long ll;
typedef pair<int, int> PII;
const int N = 5e5 + 10;
const int INF = 2e9;
int n, k;
int a[N], b[N], c[N], f[N];
int LIS(int a[], int n)
{
if (n == 0)
return 0;
f[1] = a[1];
int len = 1;
for (int i = 2; i <= n; i++)
{
if (a[i] >= f[len])
f[++len] = a[i];
else
{
int idx = upper_bound(f + 1, f + len + 1, a[i]) - f;
f[idx] = a[i];
}
}
return len;
}
int calc(int l, int r)
{
int idx = 0;
int L = a[l], R = a[r];
for (int i = l + 1; i < r; i++)
{
if (a[i] >= L && a[i] <= R)
c[++idx] = a[i];
}
return r - l - 1 - LIS(c, idx);
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1, x; i <= n; i++)
{
cin >> x;
a[i] = x - i;
}
for (int i = 1; i <= k; i++)
cin >> b[i];
b[0] = 0, b[k+1] = n + 1;
a[0] = -INF, a[n+1] = INF;
int ans = 0;
for (int i = 1; i <= k + 1; i++)
{
int l = b[i-1], r = b[i];
if (a[l] > a[r])
{
cout << -1 << endl;
return 0;
}
ans += calc(l, r);
}
cout << ans << endl;
return 0;
}