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; }
It is your time to fight!