题目大意
给你两个长度为 n n n 的数组 A 、 B A、B A、B,求 ∑ i = 0 n − 1 C i \sum_{i=0}^{n-1}C_i ∑i=0n−1Ci, C k = m a x { A i × B j } i & j ≥ k C_k=max\{A_i\times B_j\}\ i\&j\geq k Ck=max{Ai×Bj} i&j≥k
解题思路
满足条件的
i
&
j
≥
k
i\&j\geq k
i&j≥k 比较难求,我们可以把答案记录到
i
&
j
=
k
i\&j = k
i&j=k 里面去,类似一个
d
p
dp
dp。
对于一个二进制数
100
100
100,
A
[
100
]
=
m
a
x
{
A
[
101
]
,
A
[
110
]
,
A
[
100
]
}
A[100] = max\{A[101],A[110],A[100]\}
A[100]=max{A[101],A[110],A[100]},
B
B
B 同理,这样
i
&
j
=
k
i\&j = k
i&j=k 中压缩的数都满足
i
&
j
≥
k
i\&j\geq k
i&j≥k,这样我们就知道两个数相乘的最大值了。
需要注意的是有负数的情况, 对于一个位置的最大值,需要求
m
a
x
×
m
i
n
,
m
a
x
×
m
a
x
,
m
i
n
×
m
i
n
,
m
i
n
×
m
i
n
max\times min, max\times max, min\times min, min \times min
max×min,max×max,min×min,min×min
最后,我们在压缩的时候需要逆序压缩,如果正序压缩的话
000
000
000 就只继承了
100
、
010
、
001
100、010、001
100、010、001的状态,而逆序的话
100
、
010
、
001
100、010、001
100、010、001 已经继承了其他状态,所以
000
000
000就能继承到所有的状态,这样才是合理的。
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;
}