题目如上:
题解:
题目意思
给你 n 个数字和一个数 p,要找一个连续的子段,使得这个子段的和除以 p 的余数最大。
比如数组 [3,4,5,6,7],p=10;选子段 [2,3],和是 4+5=9,9%10=9,余数最大,输出:2 3 9
关键想法
1. 用前缀和简化
子段 [l, r]的和除以 p 的余数是:
(S[r] - S[l-1]) % p
2. 怎么找最大的余数?
对每个位置 r(当作子段终点),我们要找一个位置 l-1(在 r 之前),让 (S[r] - S[l-1]) % p最大。
两种情况:
1.如果 S[r] ≥ 某个之前的 S[i],选最小的 S[i] → 余数 = S[r] - S[i]
2.如果 S[r] < 某个之前的 S[i],选大于 S[r] 的最小 S[i] → 余数 = S[r] - S[i] + p
实际上两种情况可以合并为:
余数 = (S[r] - S[i] + p) % p
我们要让这个余数最大。
算法步骤
-
用一个 map 存之前见过的前缀和值,以及它们最早出现的位置
-
左到右遍历数组,计算当前前缀和 S
-
在 map 里找:
-
如果有大于 S 的数,选最小的那个
-
如果没有,选最小的数
-
用公式算出余数,更新最大余数
-
如果当前 S 没在 map 里,把它加进去(记下当前位置)
-
最后输出最大余数和对应的子段位置
AC码如下:
void solve() {
int n, p, sum = 0, i;
cin >> n >> p;
map<int, int> mp;
array<int, 3> ans = {0, 0, 0}; // {余数, 起点, 终点}
mp[0] = 0;
for (i = 1; i <= n; i++) {
int num;
cin >> num;
sum = (sum + num) % p;
auto it = mp.upper_bound(sum);
int key;
if (it == mp.end())
key = mp.begin()->first;
else
key = it->first;
int value = (sum - key + p) % p;
array<int, 3> a = {value, mp[key], i - 1};
ans = max(ans, a);//用array可以直接求,方便
if (mp.find(sum) == mp.end())
mp[sum] = i;
}
cout << ans[1] << " " << ans[2] << " " << ans[0] << endl;
}
signed main() {
//vector<vector<int>>a(n,vector<int>(m)); 二维构造
//cout << fixed << setprecision(10); 固定小数输出
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) solve();
return 0;
}
如有错误感谢指出和纠正!

京公网安备 11010502036488号