D. Maximum Sum of Products
题意:给出两个长度为n的数组a,b,要求翻转a的一个连续区间使得 ∑ i = 1 n a i ∗ b i \sum_{i=1}^na_i*b_i ∑i=1nai∗bi最大,求最大值。
1 ≤ n ≤ 5000 , 1 ≤ a i , b i ≤ 1 0 7 1\leq n \leq 5000,1\leq a_i,b_i \leq 10^7 1≤n≤5000,1≤ai,bi≤107
题解:
由于n是5000,可以考虑枚举l,r,显然维护 a i ∗ b i a_i*b_i ai∗bi的前缀和可以O(1)询问[1,l-1]和[r+1,n]区间的贡献。
设
d
p
[
l
]
[
r
]
dp[l][r]
dp[l][r]为翻转[l,r]区间之后的贡献,
d
p
[
l
]
[
r
]
dp[l][r]
dp[l][r]具有可扩充性,即可以快速递推到
d
p
[
l
−
1
]
[
r
+
1
]
dp[l-1][r+1]
dp[l−1][r+1]
d
p
[
l
]
[
r
]
=
d
p
[
l
+
1
]
[
r
−
1
]
+
a
[
l
]
∗
b
[
r
]
+
a
[
r
]
∗
b
[
l
]
dp[l][r]=dp[l+1][r-1]+a[l]*b[r]+a[r]*b[l]
dp[l][r]=dp[l+1][r−1]+a[l]∗b[r]+a[r]∗b[l]
状态空间的边界为
d
p
[
i
]
[
i
]
=
a
[
i
]
∗
b
[
i
]
dp[i][i]=a[i]*b[i]
dp[i][i]=a[i]∗b[i]
#include <bits/stdc++.h>
#define debug(x) cout<<#x"=" <<x<<'\n'
using ll=long long;
using namespace std;
template<typename T> void read(T &x){
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
const int M=5005;
ll a[M],b[M],sum[M];
ll dp[M][M];
ll ask(int l,int r)
{
if(dp[l][r]!=-1) return dp[l][r];
if(l==r) return a[l]*b[l];
if(l+1==r) return a[l]*b[r]+a[r]*b[l];
ll ans=ask(l+1,r-1);
ans+=a[l]*b[r]+a[r]*b[l];
return dp[l][r]=ans;
}
int main()
{
memset(dp,-1,sizeof(dp));
int n;
read(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) read(b[i]);
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i]*b[i];
}
ll res=sum[n];
for(int l=1;l<=n;l++)
{
for(int r=l+1;r<=n;r++)
{
res=max(res,sum[l-1]-sum[r]+sum[n]+ask(l,r));
}
}
printf("%lld",res);
return 0;
}
思维历程:
由于 n ≤ 5000 n \leq 5000 n≤5000
考虑建立起一个 n ∗ n n*n n∗n的二维表s, s [ i ] [ j ] = a i ∗ b j s[i][j]=a_i*b_j s[i][j]=ai∗bj
那么如果不翻转区间的话,贡献就是主对角线元素的乘积
考虑n=5的情况:
如果翻转了2-4区间那么就是将对应段旋转45度
显然,我们可以预处理出每条副对角线的前缀和
于是便可以O(1)询问每一对的l,r
于是发现了翻转区间的贡献的递推关系。
显然,我们可以预处理出每条副对角线的前缀和
于是便可以O(1)询问每一对的l,r
于是发现了翻转区间的贡献的递推关系。