题目链接:
C - Index × A(Continuous ver.)
题意:
给定长度为m的数组A,找到一个给定长度为n的连续的A的子数组B使得i*B[i]的和最大。
分析:
数据范围为2e5,暴力枚举的 做法是无法通过的,考虑
做法。虽然题目求的是i*B[i]的和,i是B数组的下标(从1开始),但是可以看成在A数组中找到长度为B的一个连续子序列,使得A[i]*i(这里i是A数组的下标)最大。
证明:
如果 为最大值,假设子序列从pos开始,那么将其下标i转化成B数组中的下标,(这就是题目要求)后
也是最大的,因为其中累加的每一项的乘号后的数组A部分是不变的,变的只是它前面的系数,而这个系数始终是递增的,因此不影响原式的单调性,前面式子取最大值,后面的式子同步取最大值。
解答:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
#define endl '\n'
int a[N], q[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
ll tmp = 0, ans = 0;
int begin, end = m;
int head = 1, tail = 1; //队列的头指针和尾指针
//这里要手写队列因为题目给出的长度2e5超出了stl::queue的最大范围会RE
for (int i = 1; i <= n; i++)
{
if (tail - head + 1 == m) //当队列的长度为m
{
int x = q[head++]; //拿出队头元素并弹出
tmp -= x * (i - m); //队列向右滑动,当前值减去原队头对于项
}
q[tail++] = a[i]; //入队
tmp += i * a[i];
if (tmp > ans)
{
end = i; //记录最大值对应的子序列的终点下标
ans = tmp;
}
}
ans = 0, begin = end - m + 1; //推得起点下标
for (int i = begin; i <= end; i++) //算出转化成B数组下标后的答案
ans += (i - begin + 1) * a[i];
cout << ans;
return 0;
}
这个解法是能通过题面给出的两个样例的,但是提交后发现仅对了两个点,再一看题目的数据范围,原来数组A中可能有负数,与正数的情况恰好相反,这也就意味着单调性不成立。此解法out
(其实也并不需要额外使用队列存储,可直接通过下标计算把头的值减去)
但是!如果数组A全为正数,这个解法就是正确的。
接下来我们看正解。首先要注意到这样一个规律:
这就意味着可以通过以 i 为字串起点的累加值(题目要求)推出下一个即 i+1 的累加值
记以 i 为字串起点的 为ts[i],得到递推关系:
发现后面这个可以用前缀和优化。
我们初始化求出以1为起点的ts[1]就可在线性时间推得所有的子序列ts值,在这个过程中更新最大值。
注意前缀和数组和答案要开long long!
但是!这样还是没过,因为 这项也会溢出int范围,这个坑我找了很久。为了省去强制类型转化a数组也直接开long long!所以说还是
不开long long见祖宗啊。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
#define endl '\n'
ll a[N], fsum[N], ts[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
fsum[i] = fsum[i - 1] + a[i - 1]; //求前缀和,这里包含的是到第i-1项的和
}
for (int i = 1; i <= m; i++)
ts[1] += i * a[i];
ll ans = ts[1]; //初始化ans
for (int i = 2; i <= n - m + 1; i++)
{
//注意下标QaQ
ts[i] = ts[i - 1] - (fsum[i - 1 + m] - fsum[i - 1]) + m * a[i - 1 + m];
ans = max(ans, ts[i]);
}
cout << ans;
return 0;
}