2021杭电多校第二场 1011——I love max and multiply

题目大意

给你两个长度为 n n n 的数组 A 、 B A、B AB,求 ∑ i = 0 n − 1 C i \sum_{i=0}^{n-1}C_i i=0n1Ci 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&jk

解题思路

满足条件的 i & j ≥ k i\&j\geq k i&jk 比较难求,我们可以把答案记录到 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&jk,这样我们就知道两个数相乘的最大值了。
需要注意的是有负数的情况, 对于一个位置的最大值,需要求 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 100010001的状态,而逆序的话 100 、 010 、 001 100、010、001 100010001 已经继承了其他状态,所以 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值