CF EDU 97 E - Make It Increasing

LIS

E - Make It Increasing

题意

给定数组 $a $, (n <= 5e5), 有一个集合 b ,b 里面存的是 a 数组的某些下标,这些位置的 a 的值不能改变

其余位置可花 1 代价变为任意一个整数,求让 a 变成严格单调递增的最小代价;若不可以则输出 -1

思路

有个套路是若让数组是严格单调递增的,可以令 \(a_i=a_i-i\), 之后把 \(a\) 变成非严格单调递增就可满足原来的 a 是严格单调递增的

这样就可以把数组分为若干区间,每个区间的两端是不能改变的位置,设两端下标为 l, r

  1. \(a[l] >a[r]\), 则可直接输出 -1
  2. \(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;
}
posted @ 2022-08-14 18:53  hzy0227  阅读(11)  评论(0编辑  收藏  举报