Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2) F.Bits And Pieces(高維前綴和sosdp)

題目

長度爲n(3<=n<=1e6)的數組a[],0<=ai<=2e6

求最大的ai|(aj&ak)的值,滿足三元組i<j<k

思路來源

https://blog.csdn.net/Ratina/article/details/100100711?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

題解

考慮可以枚舉一維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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章