【Codeforces】Educational Codeforces Round 108D. Maximum Sum of Products | 双指针

130 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第32天,点击查看活动详情

【Codeforces】Educational Codeforces Round 108D. Maximum Sum of Products | 双指针

题目链接

Problem - 1519D - Codeforces

题目

image.png

题目大意

给定两个长度为 nn 的正整数数组 a1,a2,a3,...,ana_1,a_2,a_3,...,a_nb1,b2,b3,...,bnb_1,b_2,b_3,...,b_n。定义操作为选择两个下标 1lrn1\le l\le r\le n,把 aa 数组的子串 al,al+1,...,ara_l,a_{l+1},...,a_r 翻转,即把 aa 数组变为

a1,a2,...al1,ar,ar1,ar2,...,al+1,al,ar+1,...,ana_1,a_2,...a_{l-1},a_r,a_{r-1},a_{r-2},...,a_{l+1},a_l,a_{r+1},...,a_n

操作完成后,更新 aa 数组的下标,使其下标仍为 1,2,...,n1,2,...,n 递增排列。

求最多进行一次操作后,最大的能取得的 i=1nai×bi\sum_{i=1}^{n}{a_i\times b_i}

思路

nn 是 5000,时间复杂度 O(n2)O(n^2) 的做法是可以接受的。

首先我们可以预处理出不进行操作的 a1,a2,a3,...,ana_1,a_2,a_3,...,a_nb1,b2,b3,...,bnb_1,b_2,b_3,...,b_n 对应位置相乘得到的积的前缀和。这样对于一个翻转的区间 l,rl,r,我们就可以 O(1)O(1) 求出未翻转部分的答案是 suml1+sumnsumrsum_{l-1}+sum_n-sum_r

如果我们直接枚举 aa 数组中翻转的区间,每次进行区间翻转后,两数组对应相乘的关系都会发生巨大的改变,我们将不得不重新计算。

为了减小时间复杂度,我们需要考虑怎样最大程度的利用我们求出来的值。如果我们先枚举翻转的对称轴,再枚举翻转的半径,则半径增大 11,对应相乘的关系仅有两端的元素会发生改变,可以 O(1)O(1) 求出变化量。考虑到翻转的区间可能是奇数也可能是偶数,我们枚举对称轴时可以使用两个变量。

代码

#include <stdio.h>
#include <algorithm>
using namespace std;
using LL=long long;
const int N=5001;
const LL mod=998244353;
LL a[N],b[N],ans=0,sum[N];
int n;
void solve()
{
	
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d",&a[i]);
	for (int i=1;i<=n;++i)
	{
		scanf("%d",&b[i]);
		ans+=a[i]*b[i];
		sum[i]=sum[i-1]+a[i]*b[i];
	}
	LL qwq=0;
	for (int lm=1,rm=1;rm<=n;)
	{
		if (lm==rm) qwq=a[lm]*b[rm];
		else qwq=0;
		for (int r=(lm==rm);lm-r>=1&&rm+r<=n;r++)
		{
			qwq+=a[lm-r]*b[rm+r]+a[rm+r]*b[lm-r];
			ans=max(ans,qwq+sum[lm-r-1]+sum[n]-sum[rm+r]);
		}
		if (lm==rm) rm++;
		else lm++;
	}
	printf("%lld\n",ans);
}
int main()
{
	solve();
	return 0;
}