#P2954. 第1题-魔法环交替求和问题
-
1000ms
Tried: 49
Accepted: 10
Difficulty: 5
所属公司 :
阿里
时间 :2025年5月12日-阿里国际(开发岗)
-
算法标签>思维
第1题-魔法环交替求和问题
题解
题面描述
小红有一个长度为 n 的魔法环,环上依次有数字 a1,a2,…,an。她想通过切开并对线性序列应用交替求和,获得最佳魔法值,具体操作如下:
-
选定一个断开位置 k (1≤k≤n),将环从此位置切开,得到线性序列:

-
根据线性序列 bj 计算其交替求和:

小红希望在所有可能的断开位置中,使 S 的值最大。请计算并输出此最大值。
思路
我们需要对长度 n 的环状数组不同断开位置进行旋转,然后计算交替求和,并找到最大值。直接对每个 k 都计算一次交替求和,复杂度 O(n2),在 n≤2×105 时无法接受。
关键在于利用递推关系在 O(1) 时间内从 Sk 更新到 Sk+1。设
Sk = ∑i=0n−1 (−1)ia(k+i−1modn)+1
即从第 k 个元素开始的交替求和。可推导:
Sk+1 = -(Sk - ak) + (−1)n−1ak
证明:
- Sk = ak - ak+1 + ak+2 -......+ (−1)n−1ak−1.
- 去掉第一个项并整体取相反数得到前 n−1 项的交替求和, (Sk - ak) = ak+1 - ak+2 +......+ (−1)n−2ak−1
- 最后再加上第 n 项 (−1)n−1ak 即得 Sk+1。
因此,算法流程:
-
计算初始旋转 S1 的交替求和,时间 O(n)。
-
预计算常数 c=(−1)n−1。
-
令
ans = S_1,然后遍历 k=1…n−1:Sk+1=−(Sk−ak)+cak,
并更新
ans = max(ans, S_{k+1})。 -
输出
ans。
整体复杂度 O(n),空间 O(n)。
代码实现
C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<ll> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 1. 计算 S1 = a0 - a1 + a2 - ...
ll S = 0;
for (int i = 0; i < n; i++) {
if (i % 2 == 0) S += a[i];
else S -= a[i];
}
// c = (-1)^(n-1)
ll c = (n % 2 == 1 ? 1 : -1);
ll ans = S;
// 2. 递推计算其他旋转
for (int k = 0; k < n - 1; k++) {
// 更新到 S_{k+2}
S = -(S - a[k]) + c * a[k];
ans = max(ans, S);
}
cout << ans;
return 0;
}
Python
import sys
def main():
data = sys.stdin.read().split()
n = int(data[0])
a = list(map(int, data[1:]))
# 1. 计算 S1
S = sum(a[i] if i % 2 == 0 else -a[i] for i in range(n))
# c = (-1)^(n-1)
c = 1 if n % 2 == 1 else -1
ans = S
# 2. 递推
for k in range(n-1):
S = -(S - a[k]) + c * a[k]
ans = max(ans, S)
print(ans)
if __name__ == '__main__':
main()
Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
StringTokenizer st = new StringTokenizer(br.readLine());
long[] a = new long[n];
for (int i = 0; i < n; i++) {
a[i] = Long.parseLong(st.nextToken());
}
// 1. 计算 S1
long S = 0;
for (int i = 0; i < n; i++) {
if (i % 2 == 0) S += a[i];
else S -= a[i];
}
// c = (-1)^(n-1)
long c = (n % 2 == 1) ? 1 : -1;
long ans = S;
// 2. 递推其他旋转
for (int k = 0; k < n - 1; k++) {
S = -(S - a[k]) + c * a[k];
ans = Math.max(ans, S);
}
System.out.println(ans);
}
}
题目内容
小红有一个长度为 n 的魔法环,环上依次有数字 a1,a2,…,an 。 她想通过切开并对线性序列应用交替求和,获得最佳魔法值具体步骤:
1.选定一个断开位置 k(1≤k≤n) ,循环从此位置切开,得到线性序列
b1=ak,b2=ak+1,…,bn−k+1=an,bn−k+1=an,bn−k+2=a1,...,bn=ak−1。
2.根据线性序列{bj}计算其交替求和:S=b1−b2+b3−b4+…+(−1)n−1bn 。
小红希望在所有可能的断开位置中,S 的值最大。请你计算并输出此最大值。
输入描述
第一行输入一个整数 n(1≤n≤2×105),表示魔法环上的数字个数。
第二行输入 n 个整数 a1,a2,…,an(0≤ai<n) ,代表环上各位置的初始数字。
输出描述
输出一个整数,表示小红在所有断开位置的交替求和中能获得的最大魔法值。
样例1
输入
4
1 2 3 2
输出
0
说明
对于任意断开位置,线性序列的交替求和均为 0 。
样例2
输入
6
1 2 3 2 5 0
输出
5
说明
当断开位置 k=1 时,序列 [1,2,3,2,5,0] 的交替求和为 1−2+3−2+5−0=5 ,是所有位置中的最大值。