前缀和 & 差分
前缀和是预处理思想的一个应用,提前计算好数组的和,需要时直接使用,加快速度。
勉强算是“数据结构”吧。
P5638 光骓者的荣耀
题目链接:P5638 光骓者的荣耀
题目大意:可跳过连续的
肯定是正着跳,暴力也是显然的,对结果取
cpp
int aa[N], n;
int ans = 0x3f3f3f3f;
for (int i = 0; i < n - 1; i++) {
int sum = 0;
for (int j = 0; j < i; j++) {
sum += aa[j];
}
for (int j = i + k; j < n - 1; j++) {
sum += aa[j];
}
ans = min(ans, sum);
}
return ans;
int aa[N], n;
int ans = 0x3f3f3f3f;
for (int i = 0; i < n - 1; i++) {
int sum = 0;
for (int j = 0; j < i; j++) {
sum += aa[j];
}
for (int j = i + k; j < n - 1; j++) {
sum += aa[j];
}
ans = min(ans, sum);
}
return ans;
优化 1
很显然超时的原因是我们每次都做了很多重复计算,主要是在求和。
怎么求和更快呢?这就需要用前缀和优化了。考虑预处理数组
cpp
int aa[N], ss[N];
// init...
int sum = 0;
for (int i = 0; i < n - 1; i++) {
sum += aa[i];
ss[i] = sum;
}
int aa[N], ss[N];
// init...
int sum = 0;
for (int i = 0; i < n - 1; i++) {
sum += aa[i];
ss[i] = sum;
}
当我们想计算
于是可以优化得到
cpp
int aa[N], n, ss[N];
// init...
int ans = 0x3f3f3f3f;
for (int i = 0; i < n - 1; i++) {
int sum = ss[i - 1] + s[n - 2] - s[i + k -1];
ans = min(ans, sum);
}
int aa[N], n, ss[N];
// init...
int ans = 0x3f3f3f3f;
for (int i = 0; i < n - 1; i++) {
int sum = ss[i - 1] + s[n - 2] - s[i + k -1];
ans = min(ans, sum);
}
仔细观察,你就会发现数组
优化 2
可能有同学想到了另一种解法,这里也讲一下。
注意到,答案可以看成两段和相加,也可以看作全部的和减去
我们可以采用一种队列的方式,维护从
cpp
// init...
int tsum = 0;
for (int i = 0; i < k; i++)
tsum += aa[i];
int ans = tsum;
for (int i = k; i < n - 1; i++) {
tsum = tsum + aa[i] - aa[i - k];
ans = max(ans, t);
}
return sum - ans;
// init...
int tsum = 0;
for (int i = 0; i < k; i++)
tsum += aa[i];
int ans = tsum;
for (int i = k; i < n - 1; i++) {
tsum = tsum + aa[i] - aa[i - k];
ans = max(ans, t);
}
return sum - ans;
差分
类似的,有前缀和,就有差分。差分数组的前缀和是原数组。
差分有什么用呢?
差分可以把“区间增加”变成“单点增加”。详细见 P2367 语文成绩。