Educational Codeforces Round 108 (Rated for Div. 2)-D. Maximum Sum of Products-题解

Educational Codeforces Round 108 (Rated for Div. 2)-D. Maximum Sum of Products

传送门
Time Limit: 2 seconds
Memory Limit: 256 megabytes

Problem Description

You are given two integer arrays a a a and b b b of length n n n.

You can reverse at most one subarray (continuous subsegment) of the array a a a.

Your task is to reverse such a subarray that the sum ∑ i = 1 n a i ⋅ b i \sum\limits_{i=1}^n a_i \cdot b_i i=1naibi is maximized.

Input

The first line contains one integer n n n ( 1 ≤ n ≤ 5000 1 \le n \le 5000 1n5000).

The second line contains n n n integers a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an ( 1 ≤ a i ≤ 1 0 7 1 \le a_i \le 10^7 1ai107).

The third line contains n n n integers b 1 , b 2 , … , b n b_1, b_2, \dots, b_n b1,b2,,bn ( 1 ≤ b i ≤ 1 0 7 1 \le b_i \le 10^7 1bi107).

Output

Print single integer — maximum possible sum after reversing at most one subarray (continuous subsegment) of a a a.

Sample Input

5
2 3 2 1 3
1 3 2 4 2

Sample Onput

29

Note

In the first example, you can reverse the subarray [ 4 , 5 ] [4, 5] [4,5]. Then a = [ 2 , 3 , 2 , 3 , 1 ] a = [2, 3, 2, 3, 1] a=[2,3,2,3,1] and 2 ⋅ 1 + 3 ⋅ 3 + 2 ⋅ 2 + 3 ⋅ 4 + 1 ⋅ 2 = 29 2 \cdot 1 + 3 \cdot 3 + 2 \cdot 2 + 3 \cdot 4 + 1 \cdot 2 = 29 21+33+22+34+12=29.

In the second example, you don’t need to use the reverse operation. 13 ⋅ 2 + 37 ⋅ 4 = 174 13 \cdot 2 + 37 \cdot 4 = 174 132+374=174.

In the third example, you can reverse the subarray [ 3 , 5 ] [3, 5] [3,5]. Then a = [ 1 , 8 , 3 , 6 , 7 , 6 ] a = [1, 8, 3, 6, 7, 6] a=[1,8,3,6,7,6] and 1 ⋅ 5 + 8 ⋅ 9 + 3 ⋅ 6 + 6 ⋅ 8 + 7 ⋅ 8 + 6 ⋅ 6 = 235 1 \cdot 5 + 8 \cdot 9 + 3 \cdot 6 + 6 \cdot 8 + 7 \cdot 8 + 6 \cdot 6 = 235 15+89+36+68+78+66=235.


题目大意

给你两个数组 a a a b b b以及一个整数 n n n,每个数组中有 n n n个正整数。你可以选择把 a a a的一个子串翻转,使得 ∑ a i ∗ b i \sum a_i*b_i aibi最大。


解题思路

字串必须是连续的!

首先来想最朴素的算法:

i 从 1 到 n:
    j 从 i 到 n:
        k 从 1 到 n:
            如果 i ≤ k ≤ j:
                ans += a[k] * b[j - k + i]
            否则:
                ans += a[k] * b[k]

这样的话复杂度为 O ( n 3 ) O(n^3) O(n3),然而 n n n的范围是 1 1 1~ 5000 5000 5000,会超时。


简单优化一下:
因为 i i i ~ j j j外面的 a [ k ] a[k] a[k] b [ k ] b[k] b[k]是一一对应的,没有翻转,故可以用一个前缀数组 q i a n Z u i qianZui qianZui,其中 q i a n Z u i [ i ] qianZui[i] qianZui[i]表示 ∑ k = 1 i a [ k ] ∗ b [ k ] \sum_{k=1}^ia[k]*b[k] k=1ia[k]b[k]。这样的话处于 l l l ~ r r r外面的所有 a [ i ] ∗ b [ i ] a[i]*b[i] a[i]b[i]的和就可以用 O ( 1 ) O(1) O(1)的时间快速得出,为 q i a n Z u i [ n ] − q i a n Z u i [ r ] + q i a n Z u i [ l − 1 ] qianZui[n]-qianZui[r]+qianZui[l-1] qianZui[n]qianZui[r]+qianZui[l1]


但是, l l l ~ r r r 中的元素还是需要一个一个计算,这就使得总体复杂度仍为 O ( n 3 ) O(n^3) O(n3)


继续优化:
既然第三层复杂度的来源是重新计算了 l l l ~ r r r 中每个 a a a b b b相乘,那么能不能找到一种方法,使得后面的 l l l ~ r r r(翻转部分)的计算基于前面的 l l l ~ r r r的计算结果之上呢?


于是我们想到,如果已知 l l l ~ r r r 的翻转后的 a a a b b b相乘的值 s 1 s_1 s1,那么就能很快求出 l − 1 l-1 l1 ~ r + 1 r+1 r+1 的翻转后的 a a a b b b相乘的值 s 2 s_2 s2,其中 s 2 = s 1 + a [ l − 1 ] ∗ b [ r + 1 ] + a [ r + 1 ] ∗ b [ l − 1 ] s_2=s_1+a[l-1]*b[r+1]+a[r+1]*b[l-1] s2=s1+a[l1]b[r+1]+a[r+1]b[l1]


所以我们第一层循环是 l l l ~ r r r 的中间的下标,之后 l − 1 l-1 l1的同时 r + 1 r+1 r+1,每次更新答案的最大值即可。


AC代码

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;
ll a[5050],b[5050];
ll qianZui[5050];
int main()
{
    int n;
    cin>>n;//每个数组有n个数
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
        cin>>b[i];
    qianZui[0]=0;//前缀初始值
    for(int i=1;i<=n;i++)
        qianZui[i]=qianZui[i-1]+a[i]*b[i];//后面一个的前缀和等于前面一个的前缀和的基础上加上a[i]*b[i]
    ll ans=qianZui[n];//目前答案最大值是一个都不翻转
    for(int mid=1;mid<=n;mid++)//区间中间从1到n
    {
        ll zhongJianZheYiKuai=a[mid]*b[mid];//中间这一段 的和
        for(int l=mid-1,r=mid+1;l>0&&r<=n;l--,r++)//l向左拓展的同时r向右拓展
        {
            zhongJianZheYiKuai+=a[l]*b[r]+a[r]*b[l];//中间这一段的和加上新拓展来的两个对应的数
            ans=max(ans,zhongJianZheYiKuai+qianZui[n]-qianZui[r]+qianZui[l-1]);//更新一下答案的最大值。如果l到r翻转的话,总和就是 中间这一段+两边的和, 其中两边的和=左边的和+右边的和, 其中左边的和就是前缀l-1,右边的和是前缀n - 前缀r。
        }
        //注意仅仅考虑上面的话翻转区间是奇数个元素,故应加上翻转区间共有偶数个元素的情况,即中间点mid暂时不包含。
        zhongJianZheYiKuai=0;//现在翻转区间为空
        for(int l=mid,r=mid+1;l>0&&r<=n;l--,r++)//翻转区间中的元素个数为偶数个,区间从mid到mid+1开始往两边拓展
        {
            zhongJianZheYiKuai+=a[l]*b[r]+a[r]*b[l];//同上
            ans=max(ans,zhongJianZheYiKuai+qianZui[n]-qianZui[r]+qianZui[l-1]);
        }
    }
    cout<<ans<<endl;
    return 0;
}

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/116333963

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tisfy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值