CF487B Strip
题意
给定一个长度为n的数组,要求把数组分为若干部分满足下面两个条件
(1):每个部分至少含有l个元素
(2):每个部分中两两数的差值的最大值不超过s
问在满足上述两个条件的情况下,最少能分成多少个部分。
\(1 \le n, l \le 10^5,1 \le s \le 10^9\)
\(\forall 1 \le i \le n, -10^9 \le a_i \le 10^9\)
Sol
一个显然的思路,设 \(f_i\) 为当前以 \(i\) 结尾,最少能分成几段。
不难发现,\(\forall 1 \le i \le n\) 的最优决策点一定是单调不降的。
随便用两个 \(St\) 表维护 \(max - min \le s\)。
for (int i = m; i <= n; i++) {
while (query(tp, i) > k)
tp++;
if (i - tp + 1 >= m)
f[i] = min(f[i], f[tp - 1] + 1);
}
Wrong answer on test 7
为什么?
我们将 \(f_i = inf\) 的 \(i\) 和 \(tp\) 输出出来。
901 878
902 878
903 878
904 878
905 878
906 878
907 878
908 878
909 878
910 900
911 900
912 900
913 900
914 900
915 900
916 900
917 900
918 900
919 900
920 900
921 900
922 900
923 900
924 900
925 900
926 900
927 900
928 900
929 900
930 900
931 900
932 900
...
此时 \(tp\) 满足 \(query(tp, i) \le k\),但是当前 \(f_{tp} = inf\),被卡在这里了。
所以我们要加一个判断。
while (query(tp + 1, i) > k || (i - tp >= m && f[tp] == inf))
tp++;
时间复杂度:\(O(n)\)
Code
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <cmath>
#include <vector>
#define int long long
using namespace std;
#ifdef ONLINE_JUDGE
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;
#endif
int read() {
int p = 0, flg = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') flg = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
p = p * 10 + c - '0';
c = getchar();
}
return p * flg;
}
void write(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
const int N = 1e5 + 5, inf = 4e18;
array <int, N> s, lg;
void init(int n) {
for (int i = 1; i <= n; i++)
lg[i] = log2(i);
}
namespace St1 {
array <vector <int>, N> f;
int query(int x, int y) {
int len = lg[y - x + 1];
return min(f[x][len], f[y - (1 << len) + 1][len]);
}
void init(int n) {
f.fill(vector <int> (21, inf));
for (int i = 1; i <= n; i++)
f[i][0] = s[i];
for (int j = 1; j < 21; j++)
for (int i = 1; i + (1 << j - 1) <= n; i++)
f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
}
namespace St2 {
array <vector <int>, N> f;
int query(int x, int y) {
int len = lg[y - x + 1];
return max(f[x][len], f[y - (1 << len) + 1][len]);
}
void init(int n) {
f.fill(vector <int> (21, -inf));
for (int i = 1; i <= n; i++)
f[i][0] = s[i];
for (int j = 1; j < 21; j++)
for (int i = 1; i + (1 << j - 1) <= n; i++)
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
}
int query(int x, int y) {
return St2::query(x, y) - St1::query(x, y);
}
array <int, N> f;
signed main() {
int n = read(), k = read(), m = read();
for (int i = 1; i <= n; i++)
s[i] = read();
init(n);
St1::init(n), St2::init(n);
int tp = 0;
f.fill(inf);
f[0] = 0;
for (int i = m; i <= n; i++) {
while (query(tp + 1, i) > k || (i - tp >= m && f[tp] == inf))
tp++;
if (i - tp >= m)
f[i] = min(f[i], f[tp] + 1);
}
write(f[n] == inf ? -1 : f[n]), puts("");
return 0;
}