1 条题解
-
0
📝 题目大意
对于序列 ,依次对 执行:将当前序列的最大值加到 上。定义 为操作结束后所有元素之和。给定长度为 的数组 ,对每个 ,求前缀 的 。
💡 解题思路
-
题目分析: 意味着每个 必须 或均摊 回答。暴力模拟每次操作是 ,不可行。需要挖掘操作过程中最大值的变化规律,推导封闭公式。
-
算法推导:
- 设前缀长度为 ,初始最大值为 ,前缀和记为 (约定 )。
- 观察每次操作后最大值的变化:
- 第 步:当前 为 ,加到 上, 变为 。新 为 (因为 ,故 )。
- 第 步:当前 为 ,加到 上, 变为 。新 为 。
- 依此类推,第 步时,当前 为 ,加到 上后 变为 ,且 成为新的最大值。
- 因此操作结束后,每个 最终值为 。
- 最终总和:$$f(a) = \sum_{i=1}^{k} (A_i + M + S_{i-1}) = \underbrace{\sum_{i=1}^{k} A_i}_{S_k} + k \cdot M + \underbrace{\sum_{i=0}^{k-1} S_i}_{\text{前缀和的前缀和}}$$
- 对应 std.cpp 中的三个数组:
z[i]:,即前 个元素的前缀和;x[i]:,即前 个元素的最大值;y[i]:,即前缀和的前缀和(y[i] = y[i-1] + z[i-1])。
- 对于每个 ,答案直接由公式 计算。
-
边界与细节:
- ,保证 ,最大值单调递增,归纳成立。
- 数据范围较大:,。最坏情况下 ,,必须使用
long long(C++ 中 64 位有符号整数,上限约 ),不会溢出。 - 公式对 同样成立:,符合预期(唯一元素加自身)。
⏱️ 复杂度分析
- 时间复杂度:。预处理三个前缀数组各需一次线性扫描,回答 个询问每次 ,总计 。
- 空间复杂度:。需要三个长度为 的辅助数组
z、x、y。
💻 标准代码 (C++)
#include<bits/stdc++.h> using namespace std; #define ll long long int main(){ int n; cin>>n; // c: 输入数组 // z[i]: 前 i 个元素的前缀和 S_i(z[0]=0) // x[i]: 前 i 个元素的最大值 M_i(x[0]=0) // y[i]: 前缀和的前缀和 sum_{j=0}^{i-1} S_j(y[0]=0) vector<ll>c(n),z(n+1,0),x(n+1,0),y(n+1,0); for(int i=0;i<n;i++)cin>>c[i]; for(int i=1;i<=n;i++){ z[i]=z[i-1]+c[i-1]; // 前缀和:S_i = S_{i-1} + A_i x[i]=max(x[i-1],c[i-1]); // 前缀最大值:M_i = max(M_{i-1}, A_i) y[i]=y[i-1]+z[i-1]; // 前缀和的前缀和:累加 S_{i-1} } for(int k=1;k<=n;k++){ ll m=x[k]; // 当前前缀的最大值 M ll cns=z[k]+1LL*k*m+y[k]; // 公式:S_k + k*M + sum_{i=0}^{k-1} S_i cout<<cns<<'\n'; } return 0; } -
信息
- ID
- 864
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 上传者