2021杭电多校补题(2)

1. I love max and multiply

题意:

给出两个数组 a a a b b b ,定义 c c c数组 c k = m a x ( a i ⋅ b j ) c_k =max(a_i \cdot b_j) ck=max(aibj) i & j ≥ k i \& j \geq k i&jk。求 ∑ i = 0 n − 1 c i \sum\limits_{i=0}^{n-1} c_i i=0n1ci

题解:

先考虑二进制下 k k k​为 i & j i\&j i&j​ 的子集。 设 d p 1 [ i ] [ 1 ] dp1[i][1] dp1[i][1]​ 表示 i i i​ 或 i i i​ 的父亲集合传给 i i i​ 的最大的 a a a​, 设 d p 1 [ i ] [ 0 ] dp1[i][0] dp1[i][0]​ 表示 i i i​ 或 i i i​ 的父亲集合传给 i i i​ 的最小的 a a a​, 设 d p 2 [ i ] [ 1 ] dp2[i][1] dp2[i][1]​ 表示 i i i​ 或 i i i​ 的父亲集合传给 i i i​ 的最大的 b b b​, 设 d p 2 [ i ] [ 0 ] dp2[i][0] dp2[i][0]​ 表示 i i i​ 或 i i i​ 的父亲集合传给 i i i​ 的最小的 b b b​。那么怎么快速将值传递给子集呢。我们可以先求出n-1的位数,然后将每一位都先赋值为1,然后 b f s bfs bfs ,每次都去掉某一位上的1,就可以做到传递给子集。

最后一定要注意的是,我们此时求出来的 c k c_k ck 只满足 k k k i & j i\& j i&j 的子集,并不满足 k ≤ i & j k \leq i\& j ki&j ,所以要取个后缀最大值。

代码:

#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=998244353;
const int MAXN=2e6+5;
const int inf=0x3f3f3f3f;
int vis[MAXN];
ll dp1[MAXN][2];
ll dp2[MAXN][2];
ll ans[MAXN];
queue<int>q;
int main()
{
    
    int t;
    scanf("%d",&t);
    while(t--)
    {
        while(!q.empty()) q.pop();
        int n;
        scanf("%d",&n);
        for(int i=0;i<=n-1;i++){
            int a;
            scanf("%d",&a);
            dp1[i][0]=dp1[i][1]=a;
        }
        for(int i=0;i<=n-1;i++){
            int b;
            scanf("%d",&b);
            dp2[i][0]=dp2[i][1]=b;
        }
        int temp=n-1;
        int cnt=1;
        int sum=0;
        while(temp)
        {
            sum+=cnt;
            temp/=2;
            cnt*=2;
        }
        q.push(sum);
        //cout<<sum<<endl;
        for(int i=0;i<=sum;i++)
        {
            vis[i]=0;
        }
        vis[sum]=1;
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            temp=now;
            cnt=1;
            while(now)
            {
                if(now&1)
                {
                    if(temp-cnt<=n-1&&temp<=n-1)
                    {
                        dp1[temp-cnt][1]=max(dp1[temp-cnt][1],dp1[temp][1]);
                        dp1[temp-cnt][0]=min(dp1[temp-cnt][0],dp1[temp][0]);
                        dp2[temp-cnt][1]=max(dp2[temp-cnt][1],dp2[temp][1]);
                        dp2[temp-cnt][0]=min(dp2[temp-cnt][0],dp2[temp][0]);
                    }
                    if(!vis[temp-cnt])
                    {
                        q.push(temp-cnt);
                        vis[temp-cnt]=1;
                    }
                }
                now/=2;
                cnt*=2;
            }
        }
        ans[n]=-1e18;
        for(int i=n-1;i>=0;i--)
        {
            ll c1=(dp1[i][1]*dp2[i][1]);
            ll c2=(dp1[i][0]*dp2[i][0]);
            ll c3=(dp1[i][0]*dp2[i][1]);
            ll c4=(dp1[i][1]*dp2[i][0]);
            ll maxx=max(c1,c2);
            maxx=max(maxx,c3);
            maxx=max(maxx,c4);
            ans[i]=maxx;
            //ans[i]=max(ans[i],ans[i+1]);
        }
        ll res=0;
        for(int i=0;i<n;i++)
        {
            res=(res+ans[i]%mod+mod)%mod;
        }
        printf("%lld\n",(res+mod)%mod);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值