题意:如果一个序列在删掉最多一个数后严格递增,那么这个序列就是一个合法序列。现给你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
*/