題目
長度爲n(3<=n<=1e6)的數組a[],0<=ai<=2e6
求最大的ai|(aj&ak)的值,滿足三元組i<j<k
思路來源
題解
考慮可以枚舉一維ai,
問題轉化爲,在ai沒有的那些二進制位裏,從高位到低位選,
假設當前貪心選取的二進制位爲bit,bit對應的任意超集裏,是否存在兩個位置j,k比i大
由於只需要考慮兩個位置,故維護pair dp[i]爲i對應的二進制狀態的最大的兩個位置
由於對於bit要考慮其超集,故令超集對其子集更新,
全下放的複雜度不能接受,考慮每次只舍一個二進制位,倒序下放
複雜度O(n+m*2^m),其中m爲二進制位20
O(m*2^m)預處理,然後枚舉一維ai,
從高位到低位貪心選位,然後考慮這個額外選的二進制狀態裏的最大兩個位置j,k,
如果均大於就或上這一位,否則就保留着之前的狀態,繼續往低位選
代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10,M=2e6+10;
#define fi first
#define se second
typedef pair<int,int> P;
int n,v,ans,a[N];
P dp[M];
//dp[i}:表示i的超集(即子集中包含i)的最大位置和次大位置
void merge(P &a,int x){
if(x>a.fi){
a.se=a.fi;
a.fi=x;
}
else if(x>a.se && x!=a.fi){
a.se=x;
}
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
merge(dp[a[i]],i);
}
for(int i=20;i>=0;--i){
for(int j=M-1;j>=0;--j){
if(j>>i&1){//去掉1位的子集 每個向相鄰的子集下放答案
merge(dp[j^(1<<i)],dp[j].fi);
merge(dp[j^(1<<i)],dp[j].se);
}
}
}
for(int i=0;i<n-2;++i){//i,n-2,n-1
int s=0;
for(int j=20;j>=0;--j){
if(!(a[i]>>j&1) && dp[s|(1<<j)].fi>i && dp[s|(1<<j)].se>i){
s|=(1<<j);
}
}
ans=max(ans,a[i]|s);
}
printf("%d\n",ans);
return 0;
}