JZOJ4828. 最大值

题目大意

给定 n 个正整数的序列a和一个运算 op
op 可以是 xor,and,or 中的一个
求最大的 ai op aj (1i<jn)
T 组数据。

Data Constraint
T6,n105,ai220

题解

xor 操作显然是最简单的,直接在tire上贪心的跑一下。
or,and 相对复杂。先思考 and 操作。
考虑从高位向低位贪心,若当前位置是1,显然需要找到另一个数当前位是1。若是0,那么当前为是0/1的数都可能最优,所以不能在trie上跑。预处理数组 fx 表示在序列 a 中,有多少个数二进制下1的位置包含了x二进制下1的位置。
这个可以分治计算。
设当前分治的区间 [0,2x] ,在我们计算出左右子区间的贡献后, fx=fx+fx+2x1 , 0x2x1 。这是因为包含了 x+2x1 的必定也包含了 x
有了f之后就可以按位贪心了。
or 同理。

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;

#define N 100000 + 10
#define M 2000000 + 10
const int MAXN = 20 ;
struct Trie {
    int Son[2] ;
} T[MAXN*N] ;

int a[N] , f[M] ;
int Case , n , op , Cnt ;
int ans ;

inline int Read() {
    int ret = 0 ;
    char ch = getchar() ;
    while ( ch < '0' || ch > '9' ) ch = getchar() ;
    while ( ch >= '0' && ch <= '9' ) ret = ret * 10 + ch - '0' , ch = getchar() ;
    return ret ;
}

inline int NewNode() {
    Cnt ++ ;
    T[Cnt].Son[0] = T[Cnt].Son[1] = 0 ;
    return Cnt ;
}

void Insert( int x , int v , int dep ) {
    if ( dep < 0 ) return ;
    int w = (v >> dep) & 1 ;
    if ( !T[x].Son[w] ) T[x].Son[w] = NewNode() ;
    Insert( T[x].Son[w] , v , dep - 1 ) ;
}

void Search( int x , int v , int dep , int sum ) {
    if ( dep < 0 ) { ans = max( ans , sum ) ; return ; }
    int w = (v >> dep) & 1 ;
    if ( T[x].Son[!w] ) Search( T[x].Son[!w] , v , dep - 1 , sum | (1 << dep) ) ;
    else Search( T[x].Son[w] , v , dep - 1 , sum ) ;
}

void SolXOR() {
    int UP = 20 ;
    for (int i = n ; i >= 1 ; i -- ) {
        Search( 1 , a[i] , UP , 0 ) ;
        Insert( 1 , a[i] , UP ) ;
    }
    printf( "%d\n" , ans ) ;
}

void DIV( int l , int r , int w ) {
    if ( l == r ) return ;
    int mid = (l + r) / 2 ;
    DIV( l , mid , w - 1 ) ;
    DIV( mid + 1 , r , w - 1 ) ;
    for (int i = l ; i <= mid ; i ++ ) f[i] += f[i+(1<<(w-1))] ;
}

void Pre() {
    memset( f , 0 , sizeof(f) ) ;
    for (int i = 1 ; i <= n ; i ++ ) f[a[i]] ++ ;
    DIV( 0 , (1 << 20) - 1 , 20 ) ;
}

void SolAND() {
    for (int i = 1 ; i <= n ; i ++ ) {
        int sum = 0 ;
        for (int k = 20 ; k >= 0 ; k -- ) {
            int x = (a[i] >> k) & 1 ;
            if ( !x ) continue ;
            if ( f[sum|(1<<k)] > 1 ) sum |= 1 << k ;
        }
        ans = max( ans , sum ) ;
    }
    printf( "%d\n" , ans ) ;
}

void SolOR() {
    for (int i = 1 ; i <= n ; i ++ ) {
        int sum = 0 , bas = 0 ;
        for (int k = 20 ; k >= 0 ; k -- ) {
            int x = (a[i] >> k) & 1 ;
            if ( x ) sum |= 1 << k ;
            else {
                if ( f[bas|(1<<k)] ) bas |= 1 << k , sum |= 1 << k ;
            }
        }
        ans = max( ans , sum ) ;
    }
    printf( "%d\n" , ans ) ;
}

int main() {
    freopen( "maximum.in" , "r" , stdin ) ;
    freopen( "maximum.out" , "w" , stdout ) ;
    scanf( "%d" , &Case ) ;
    while ( Case -- ) {
        T[1].Son[0] = T[1].Son[1] = 0 ;
        Cnt = 1 , ans = 0 ;
        n = Read() , op = Read() ;
        for (int i = 1 ; i <= n ; i ++ ) a[i] = Read() ;
        if ( op == 2 ) SolXOR() ;
        else {
            Pre() ;
            if ( op == 1 ) SolAND() ;
            else SolOR() ;
        }
    }
    return 0 ;
}

以上.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值