Educational Codeforces Round 108 (Rated for Div. 2) D. Maximum Sum of Products(对角线前缀和/区间DP/好题)
You are given two integer arrays a and b of length n.
You can reverse at most one subarray (continuous subsegment) of the array 𝑎a.
Your task is to reverse such a subarray that the sum \(\Sigma_{i = 1}^na_i\times b_i\) is maximized.
Input
The first line contains one integer n (1≤n≤5000).
The second line contains 𝑛n integers a1,a2,…,an (1≤ai≤10^7).
The third line contains 𝑛n integers b1,b2,…,bn (1≤bi≤10^7).
Output
Print single integer — maximum possible sum after reversing at most one subarray (continuous subsegment) of 𝑎a.
Examples
input
Copy
5
2 3 2 1 3
1 3 2 4 2
output
Copy
29
input
Copy
2
13 37
2 4
output
Copy
174
input
Copy
6
1 8 7 6 3 6
5 9 6 8 8 6
output
Copy
235
很容易就想到可以枚举反转的左右端点,反转区间两边的乘积和可以通过前缀和计算,但是中间的反转部分如果O(N)进行求解的话显然会T。因此考虑如何处理。通过观察发现,如果设\(mtx[i][j] = a[i]\times b[j]\),同时设反转的左右端点分别为l和r, 那么反转序列的乘积和为\(mtx[l][r] + mtx[l + 1][r - 1] + ... + mtx[r][l]\),放到矩阵里看会发现这个和反对角线是平行的,可以和处理普通的前缀和一样预处理得到,这样我们的计算反转区间值就能优化到O(1)了。对角线前缀和计算可以自行百度QAQ
同校大佬说了一个区间DP的做法,\(dp[i][j] = dp[i + 1][j - 1] + a[i]\times b[j] + a[j]\times b[i]\)也很巧妙。
#include <bits/stdc++.h>
using namespace std;
long long n, a[5005], b[5005], p[5005], sum[5005], ans = 0, sum1[5005][5005];//反对角线前缀和
int main() {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) cin >> b[i];
sum[0] = 0;
for(int i = 1; i <= n; i++) {
p[i] = a[i] * b[i];
sum[i] = sum[i - 1] + p[i];
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
sum1[i][j] = sum1[i-1][j+1] + a[i] * b[j];
}
}
for(int i = 1; i <= n; i++) {
for(int j = i; j <= n; j++) {
int l = i, r = j;
long long tmp = sum[l - 1] + sum[n] - sum[r];
tmp += sum1[r][l]-sum1[l - 1][r + 1];
ans = max(ans, tmp);
}
}
cout << ans;
return 0;
}