P r o b l e m \mathrm{Problem} Problem
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 a a consisting of n n 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?
T r a n s l a t i o n \mathrm{Translation} Translation
定义一个序列 A A A是 A l m o s t I n c r e a s i n g Almost\;Increasing AlmostIncreasing的,当 A A A满足从 A A A中去掉一个位置之后剩下的序列形成了一个严格递增序列。
给出一个长度为 n n n的序列,问最小需要改动多少个位置的值,使得这个序列变成 A l m o s t I n c r e a s i n g Almost\;Increasing AlmostIncreasing的。
S o l u t i o n \mathrm{Solution} Solution
解决这道题我要知道一个结论:
- 一串序列 { a i } \{a_i\} {ai}要单调递增需要改变的数值个数,那么我们所求的代价相当于序列 { a i − i } \{a_i-i\} {ai−i}中最长不下降子序列的长度。
证明:
考虑元素
a
i
a_i
ai 和
a
j
a_j
aj 能够共存的条件。
显然
a
i
a_i
ai 和
a
j
a_j
aj 是单调递增的两个元素,且单调递增序列中相邻两个元素之间增量至少为
1
1
1.
因此我们就有:
a
i
−
a
j
≥
i
−
j
(
i
>
j
)
a_i-a_j\ge i-j(i>j)
ai−aj≥i−j(i>j),则移项可以得到
a
i
−
i
≥
a
j
−
j
a_i-i\ge a_j-j
ai−i≥aj−j.
所以,我们的问题就转化为了序列 { a i − i } \{a_i-i\} {ai−i}去掉一个数以后的最长不下降子序列。
我们设 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示前i个数字中,还没有删除数字/已经删除数字的最长不下降子序列。
那么十分显然的就有:
f
[
i
]
[
0
]
=
max
{
f
[
j
]
[
0
]
}
+
1
(
a
j
−
j
≤
a
i
−
i
)
f[i][0]=\max\{f[j][0]\}+1(a_j-j\le a_i-i)
f[i][0]=max{f[j][0]}+1(aj−j≤ai−i)
f
[
i
]
[
1
]
=
max
{
f
[
j
]
[
1
]
}
+
1
(
a
j
−
j
≤
a
i
−
i
)
f[i][1]=\max\{f[j][1]\}+1(a_j-j\le a_i-i)
f[i][1]=max{f[j][1]}+1(aj−j≤ai−i)
这两步和经典的最长不下降子序列相同。
对于去掉一个元素的情况,我们发现后面的所有点下标都会减去1,因此:
f
[
i
]
[
1
]
=
max
{
f
[
j
]
[
0
]
}
+
1
(
a
j
−
j
≤
a
i
−
i
+
1
,
j
<
i
−
1
)
f[i][1]=\max\{f[j][0]\}+1(a_j-j\le a_i-i+1,j<i-1)
f[i][1]=max{f[j][0]}+1(aj−j≤ai−i+1,j<i−1)
对于值域的限制,我们用树状数组维护即可。
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
using namespace std;
const int N = 6e5;
int n, m(0), cnt(0);
int a[N], b[N], c[N], f[N][2];
map < int, int > tag;
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
void input(void)
{
n = read();
for (int i=1;i<=n;++i) {
a[i] = read() - i;
b[i] = a[i] + 1;
c[++ m] = a[i], c[++ m] = b[i];
}
sort(c + 1, c + m + 1);
for (int i=1;i<=m;++i)
{
if (c[i] != c[i-1]) tag[c[i]] = ++ cnt;
else if (i == 1) tag[c[i]] = ++ cnt;
}
for (int i=1;i<=m;++i) {
a[i] = tag[a[i]];
b[i] = tag[b[i]];
}
//for (int i=1;i<=n;++i) cout<<a[i]<<' ';cout<<endl;
//for (int i=1;i<=n;++i) cout<<b[i]<<' ';cout<<endl;
}
struct BIT
{
#define lowbit(i) (i & -i)
int S[N * 10] = {};
void add(int x, int v) {
for (int i=x;i<=cnt;i+=lowbit(i))
S[i] = max(S[i], v);
return;
}
int ask(int x) {
int res = 0;
for (int i=x;i>=1;i-=lowbit(i))
res = max(res, S[i]);
return res;
}
} tree1, tree2, tree3;
void work(void)
{
int res = 0;
for (int i=1;i<=n;++i)
{
int val1 = tree1.ask(a[i]);
int val2 = tree2.ask(a[i]);
int val3 = tree3.ask(b[i]);
f[i][0] = val1 + 1;
if (val2 > 0) f[i][1] = val2 + 1;
if (i > 2) f[i][1] = max(f[i][1], val3 + 1);
tree1.add(a[i], f[i][0]);
tree2.add(a[i], f[i][1]);
if (i > 1) tree3.add(a[i-1], f[i-1][0]);
res = max(res, max(f[i][0], f[i][1]));
}
cout << max(n - 1 - res, 0) << endl;
return;
}
int main(void)
{
input();
work();
return 0;
}