Educational Codeforces Round 39: G. Almost Increasing Array(LIS)

time limit per test  3 seconds 
memory limit per test  512 megabytes
input  standard input
output  standard output

We call an array almost increasing if we can erase not more than one element from it so that the array becomes strictly increasing (that is, every element is striclty greater than every element before it).

You are given an array a consisting of n elements. You are allowed to replace any element with any integer number (and you may do so any number of times you need). What is the minimum number of replacements you have to perform in order to make the array almost increasing?

Input

The first line contains one integer n (2 ≤ n ≤ 200000) — the number of elements in a.

The second line contains n integers a1a2, ..., an (1 ≤ ai ≤ 109) — the array a.

Output

Print the minimum number of replaces you have to perform so that a is almost increasing.

Examples
input
5
5 4 3 2 1
output
3
input
5
1 2 8 9 5
output
0


题意:如果一个序列在删掉最多一个数后严格递增,那么这个序列就是一个合法序列。现给你n个数

请问至少修改几个数字才能使这个序列合法


先假设不允许删除数字:

因为要严格递增,对于下标x<y,当且仅当a[y]-a[x]>=y-x时,这两个数就可以都不动,否则一定有一个要修改

设b[i] = a[i]-i,F[i]表示以b[i]结尾的最长非严格递增序列长度,答案就是n-F[n]


再假设可以删掉最多一个数,但是n只有1000:

同上,对于下标x<y,如果删掉的数不在(x, y)范围内,那么当且仅当a[y]-a[x]>=y-x时,这两个数可以都不修改

如果删掉的数在(x, y)范围内,那么当且仅当a[y]-a[x]>=y-x-1时,这两个数可以都不修改

考虑暴力枚举第k个数被删掉,那么前k-1数令b[i] = a[i]-i,后n-k个数令b[i] = a[i]-i+1

F[i]表示以b[i]结尾的最长非严格递增序列长度,答案就是n-F[n],复杂度O(n²logn)


可这题n=200000,考虑可不可以优化掉一个n:

其实如果能看懂上面O(n²logn)的思路,你会发现删掉第k个数后,如果单独考虑后面n-k个数或者前k-1个数

无论是b[i] = a[i]-i还是b[i] = a[i]-i+1,都不会影响序列的全序关系,考虑还是直接令b[i] = a[i]-i (i<=n)

F[i]表示以i结尾的最长非严格递增序列长度,G[i]表示倒序以i结尾的最长非严格递减序列长度

答案就是n-1-max(F[x]+G[y],  x+1<y && b[x]-1≤b[y])

暴力所有的x, y复杂度是O(n²)的,实际上可以用set或者平衡树优化为O(nlogn)

(这题哪怕是终测数据也很弱,你其实不需要暴力所有的x, y,你只需要暴力距离不超过2的x, y就可以AC)


考虑更加简单的方法:

上面无论是两次LIS+平衡树还是set都非常麻烦,其实在两次LIS的过程中就可以发现

设长度为x的LIS最大的数最小为bet[x],若求出了(1, x-2)的所有a[i]-i的bet[]值,那么在求(1, x)的所有a[i]-i+1的bet'[]值时,将bet[]合并进去二分就OK了,最后答案就是n-1-LIS,不过要特判答案为0的情况


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
LL a[200005], b[200005], bet[200005], bet2[200005], pos[200005];
int main(void)
{
	LL n, i, len, l, r, m, len2;
	scanf("%lld", &n);
	for(i=1;i<=n;i++)
	{
		scanf("%lld", &a[i]);
		b[i] = a[i]-i;
	}
	len = len2 = 0;
	for(i=1;i<=n;i++)
	{
		l = 1, r = len2+1;
		while(l<r)
		{
			m = (l+r)/2;
			if(bet2[m]<=b[i])  l = m+1;
			else  r = m;
		}
		bet2[r] = b[i];
		while(bet[r]<=b[i] && pos[r]<=i-2 && r<=len2)
			bet2[++r] = b[i];
		len2 = max(len2, r);
		b[i]--;
		l = 1, r = len+1;
		while(l<r)
		{
			m = (l+r)/2;
			if(bet[m]<=b[i])  l = m+1;
			else  r = m;
		}
		bet[r] = b[i];
		pos[r] = i;
		if(r==len+1)
			++len;
	}
	printf("%d\n", max(n-1-len2, 0ll));
	return 0;
}
/*
7
1 5 8 8 7 8 9
20
1 2 3 4 5 10 10 10 10 10 10 10 10 10 10 15 16 17 18 19
*/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值