题解:P16647 [GKS 2018 #D] Candies
题目大意
给定一个数组 \(S\) 长度为 \(n\),在其中选取一段连续区间,且需满足:
- 区间内甜度为奇数的糖果数量 \(\le O\)
- 区间总甜度 \(\le D\)
在保证以上要求的情况下使区间内甜度的和最大化。
sol
优先可以想到滑动窗口维护区间和最大,可是甜度可以为负数,所以可以在维护滑动窗口的双指针写法中加入前缀和与二分来实现。
具体实现见代码注释:
:::info[code]{open}
#include<bits/stdc++.h>
using namespace std;
const long long INF = 1e18;
int T;
int main() {
cin >> T;
for (int t = 1; t <= T; t++) {
cout << "Case #" << t << ": ";
int n, o;
long long d;
cin >> n >> o >> d;
vector<long long> x(n + 2);
long long A, B, C, M, L;
cin >> x[1] >> x[2] >> A >> B >> C >> M >> L;
vector<long long> s(n + 1);
// qzh1[i]:前i颗糖果中奇数甜度的数量
vector<int> qzh1(n + 1);
// qzh2[i]:前i颗糖果的总甜度
vector<long long> qzh2(n + 1);
for (int i = 3; i <= n; i++) {
x[i] = (A * x[i - 1] + B * x[i - 2] + C) % M;
}
for (int i = 1; i <= n; i++) {
s[i] = x[i] + L;
// 奇数糖果数量前缀和:s[i]是奇数就+1,否则+0
qzh1[i] = qzh1[i - 1] + (s[i] % 2 != 0);
// 总甜度前缀和
qzh2[i] = qzh2[i - 1] + s[i];
}
long long ans = -INF;
// set存储(前缀和值,下标),自动按前缀和升序排序
set<pair<long long, int>> st;
// 先插入前缀和0(对应前0颗糖果),这是滑动窗口的起点
st.insert({qzh2[0], 0});
// 滑动窗口左边界,初始在0位置
int l = 0;
// 枚举滑动窗口右边界 r(表示以r结尾的连续子数组)
for (int r = 1; r <= n; r++) {
// 约束1:窗口内奇数糖果数量不能超过 o
// 如果当前窗口 [l+1, r] 奇数数量超标,就移动左边界并删除对应前缀和
while (qzh1[r] - qzh1[l] > o) {
st.erase({qzh2[l], l});
l++;
}
// 约束2:总甜度 <= d
// 即 qzh2[r] - qzh2[k] <= d → qzh2[k] >= qzh2[r] - d
// 在set中找第一个 >= (qzh2[r]-d) 的前缀和
auto it = st.lower_bound({qzh2[r] - d, -1});
// 如果找到了合法的k,就更新最大甜度
if (it != st.end()) {
ans = max(ans, qzh2[r] - it->first);
}
// 把当前右端点的前缀和加入set,供后续使用
st.insert({qzh2[r], r});
}
// 如果还是负无穷,说明没有合法方案
if (ans == -INF) {
cout << "IMPOSSIBLE\n";
} else {
cout << ans << "\n";
}
}
return 0;
}
:::

浙公网安备 33010602011771号