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

83 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

@TOC

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 aa and bb of length nn.

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

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

Input

The first line contains one integer nn (1n50001 \le n \le 5000).

The second line contains nn integers a1,a2,,ana_1, a_2, \dots, a_n (1ai1071 \le a_i \le 10^7).

The third line contains nn integers b1,b2,,bnb_1, b_2, \dots, b_n (1bi1071 \le b_i \le 10^7).

Output

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

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]. Then a=[2,3,2,3,1]a = [2, 3, 2, 3, 1] and 21+33+22+34+12=292 \cdot 1 + 3 \cdot 3 + 2 \cdot 2 + 3 \cdot 4 + 1 \cdot 2 = 29.

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

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


题目大意

给你两个数组aabb以及一个整数nn,每个数组中有nn个正整数。你可以选择把aa的一个子串翻转,使得aibi\sum a_i*b_i最大。


解题思路

字串必须是连续的!

首先来想最朴素的算法:

i1n:
    jin:
        k1n:
            如果 ikj:
                ans += a[k] * b[j - k + i]
            否则:
                ans += a[k] * b[k]

这样的话复杂度为O(n3)O(n^3),然而nn的范围是11~50005000,会超时。
简单优化一下: 因为ii ~ jj外面的a[k]a[k]b[k]b[k]是一一对应的,没有翻转,故可以用一个前缀数组qianZuiqianZui,其中qianZui[i]qianZui[i]表示k=1ia[k]b[k]\sum_{k=1}^ia[k]*b[k]。这样的话处于ll ~ rr外面的所有a[i]b[i]a[i]*b[i]的和就可以用O(1)O(1)的时间快速得出,为qianZui[n]qianZui[r]+qianZui[l1]qianZui[n]-qianZui[r]+qianZui[l-1]
但是,ll ~ rr 中的元素还是需要一个一个计算,这就使得总体复杂度仍为O(n3)O(n^3)
继续优化: 既然第三层复杂度的来源是重新计算了ll ~ rr 中每个aabb相乘,那么能不能找到一种方法,使得后面的ll ~ rr(翻转部分)的计算基于前面的ll ~ rr的计算结果之上呢?
于是我们想到,如果已知ll ~ rr 的翻转后的aabb相乘的值s1s_1,那么就能很快求出l1l-1 ~ r+1r+1 的翻转后的aabb相乘的值s2s_2,其中s2=s1+a[l1]b[r+1]+a[r+1]b[l1]s_2=s_1+a[l-1]*b[r+1]+a[r+1]*b[l-1]
所以我们第一层循环是ll ~ rr 的中间的下标,之后l1l-1的同时r+1r+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;
}

同步发文于我的CSDN,原创不易,转载请附上原文链接哦~
Tisfy:letmefly.blog.csdn.net/article/det…