UVa 1451 - Average

链接

传送门

题意

给出一个长度为\(n\)的01串,找出其中长度大于等于\(L\)的每一位的平均值最大的连续子串。输出子串的左右端点。

思路

首先预处理出前缀和\(sum[i]\),设点\(P[i]=(i,S[i])\),则序列\(i~j\)上的平均值为\((S[j]-S[i])/(j-i+1)\)也就是直线\(P[j]P[i]\)的斜率。用栈维护点集,枚举终点,删除栈中的上凸点后求得当前解。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 100010;
char s[maxn];
int sum[maxn], p[maxn];

inline double cmp(int x1, int x2, int x3, int x4) {
--x1, --x3;
return (sum[x2] - sum[x1]) * (x4 - x3) - (sum[x4] - sum[x3]) * (x2 - x1);
}

int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, L;
scanf("%d%d%s", &n, &L, s + 1);
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1] + s[i] - '0';
}
int ansL = 1, ansR = L;
int i = 0, j = 0;
for (int t = L; t <= n; ++t) {
while (j - i > 1 && cmp(p[j - 2], t - L, p[j - 1], t - L) >= 0) {
--j;
}
p[j++] = t - L + 1;
while (j - i > 1 && cmp(p[i], t, p[i + 1], t) <= 0) {
++i;
}
int c = cmp(p[i], t, ansL, ansR);
if (c > 0 || (c == 0 && t - p[i] < ansR - ansL)) {
ansL = p[i]; ansR = t;
}
}
printf("%d %d\n", ansL, ansR);
}
return 0;
}