1. 程式人生 > >HihoCoder - 1496:尋找最大值(高維字首和||手動求子集)

HihoCoder - 1496:尋找最大值(高維字首和||手動求子集)

描述

給定N個數A1, A2, A3, ... AN,小Ho想從中找到兩個數Ai和Aj(i ≠ j)使得乘積Ai × Aj × (Ai AND Aj)最大。其中AND是按位與操作。  

小Ho當然知道怎麼做。現在他想把這個問題交給你。

輸入

第一行一個數T,表示資料組數。(1 <= T <= 10)  

對於每一組資料:

第一行一個整數N(1<=N<=100,000)

第二行N個整數A1, A2, A3, ... AN (0 <= Ai <220)

輸出

一個數表示答案

樣例輸入

2
3
1 2 3
4
1 2 4 5

樣例輸出

12
80

思路:Ai*Aj*(Ai&Aj)我們列舉第三部分,假設第三部分為x=Ai&Aj,然後我們取x的超集的最大值和次大值即可。

對於每個Ai我們可以列舉子集,用Ai取更新子集的最大次大值。4777ms;

#include<bits/stdc++.h>
using namespace std;
const int maxn=(1<<20)+10;
int Mx[maxn],Se[maxn]; long long ans;
int main()
{
    
int T,N,x; scanf("%d",&T); while(T--){ memset(Mx,0,sizeof(Mx)); memset(Se,0,sizeof(Se)); scanf("%d",&N); for(int i=1;i<=N;i++){ scanf("%d",&x); for(int j=x;j;j=(j-1)&x){ if(x>Mx[j]){ Se[j]
=Mx[j]; Mx[j]=x; } else if(x>Se[j]) Se[j]=x; } } ans=0; for(int i=1;i<maxn;i++) ans=max(ans,(long long)i*Mx[i]*Se[i]); printf("%lld\n",ans); } return 0; }

也可以利用高維字首和來維護最大次大值。1586ms。

(目前見到的三種:高維字首和維護了X集之和,位置的最小值,最大次大值。ORZ

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1<<20;
int Mx[maxn+10],Se[maxn+10]; ll ans;
int main()
{
    int T,N,x;
    scanf("%d",&T);
    while(T--){
        memset(Mx,0,sizeof(Mx));
        memset(Se,0,sizeof(Se));
        scanf("%d",&N);
        for(int i=1;i<=N;i++){
            scanf("%d",&x);
            if(x>Mx[x]) Mx[x]=x;
            else Se[x]=x;
        }
        for(int i=0;i<20;i++){
            for(int j=0;j<maxn;j++){
                if(!(j&(1<<i))){
                    if(Mx[j|(1<<i)]>=Mx[j]){
                        Se[j]=max(Mx[j],Se[j|(1<<i)]);
                        Mx[j]=Mx[j|(1<<i)];
                    }
                    else Se[j]=max(Mx[j|(1<<i)],Se[j]);
                }
            }
        }
        ans=0;
        for(int i=1;i<maxn;i++) ans=max(ans,(ll)i*Mx[i]*Se[i]);
        printf("%lld\n",ans);
    }
    return 0;
}