题目大意
给你两个长度为 nnn 的数组 A、BA、BA、B,求 ∑i=0n−1Ci\sum_{i=0}^{n-1}C_i∑i=0n−1Ci,Ck=max{Ai×Bj} i&j≥kC_k=max\{A_i\times B_j\}\ i\&j\geq kCk=max{Ai×Bj} i&j≥k
解题思路
满足条件的 i&j≥ki\&j\geq ki&j≥k 比较难求,我们可以把答案记录到 i&j=ki\&j = ki&j=k 里面去,类似一个dpdpdp。
对于一个二进制数 100100100,A[100]=max{A[101],A[110],A[100]}A[100] = max\{A[101],A[110],A[100]\}A[100]=max{A[101],A[110],A[100]},BBB 同理,这样 i&j=ki\&j = ki&j=k 中压缩的数都满足 i&j≥ki\&j\geq ki&j≥k,这样我们就知道两个数相乘的最大值了。
需要注意的是有负数的情况, 对于一个位置的最大值,需要求 max×min,max×max,min×min,min×minmax\times min, max\times max, min\times min, min \times minmax×min,max×max,min×min,min×min
最后,我们在压缩的时候需要逆序压缩,如果正序压缩的话 000000000 就只继承了 100、010、001100、010、001100、010、001的状态,而逆序的话 100、010、001100、010、001100、010、001 已经继承了其他状态,所以 000000000就能继承到所有的状态,这样才是合理的。
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
using namespace std;
const ll mod = 998244353;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll a[1 << 20];
ll b[1 << 20];
ll A[1 << 20];
ll B[1 << 20];
int n;
void solve(){
cin >> n;
for (int i = 0; i < n; ++i){
cin >> a[i];
A[i] = a[i];
}
for (int i = 0; i < n; ++i){
cin >> b[i];
B[i] = b[i];
}
ll m = 1;
while(m < n) m <<= 1;
for (int i = n; i <= m; ++i){
a[i] = b[i] = inf;
A[i] = B[i] = -inf;
}
for (int i = n-1; i >= 0; --i){
for(int j = 1; j < m; j <<= 1){
if(!(i&j)){
A[i] = max(A[i], A[i^j]);
B[i] = max(B[i], B[i^j]);
a[i] = min(a[i], a[i^j]);
b[i] = min(b[i], b[i^j]);
}
}
}
ll tmp = -INF;
ll ans = 0;
for(int i = n-1; i >= 0; --i){
tmp = max(tmp, A[i] * B[i]);
tmp = max(tmp, A[i] * b[i]);
tmp = max(tmp, a[i] * B[i]);
tmp = max(tmp, a[i] * b[i]);
ans = (ans + (tmp % mod) ) % mod;
}
cout << (ans + mod) % mod<< "\n";
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
qc;
int T;
cin >> T;
while(T--){
solve();
}
return 0;
}

这篇博客介绍了如何利用动态规划和位运算解决一个数组中的最大乘积问题。通过将数组元素与二进制位对应,可以有效地计算每个位置上的最大乘积,并处理负数的情况。在压缩过程中,采用逆序处理以确保所有可能的状态都被考虑。最终,通过求解每个位置的最大乘积并累加,得到整个数组的最大乘积。
3776

被折叠的 条评论
为什么被折叠?



