F. Bits And Pieces (SOSdp)

题目大意:

长度为 n ( ≤ 1 e 6 ) n(\le 1e6) n(1e6)的序列 a a a,找到最大的 a i ∣ ( a j & a k ) a_i|(a_j\&a_k) ai(aj&ak) i < j < k i<j<k i<j<k

解题思路:

  • 对于位运算球最大值的题目,一般都会贪心从高位向低位填充的方法
  • 先设可挑选的集合为: S 0 , 1 , 2 , . . . S_{0,1,2,...} S0,1,2,...
  • 从后往前枚举 a i a_i ai,然后从最高位贪心枚举,如果枚举到 a i a_i ai的第 p p p位为1,那么 a j , a k a_j,a_k aj,ak S p S_p Sp取任意都行,且 S p − 1 = S p S_{p-1}=S_{p} Sp1=Sp r e s res res更新
  • 如果第 p p p位为0,那么判断 S p S_p Sp中是否有至少两个数第 p p p位为1,将这些数放到 S p − 1 S_{p-1} Sp1中,答案会更新 r e s ∣ = 1 < < p res |= 1<<p res=1<<p,否则就 S p − 1 = S p S_{p-1}=S_p Sp1=Sp r e s res res不更新
  • 但是如果按照上面那样更新 S S S,会超时,所以要重新定义 S S S S ( 1100 ) 2 S_{(1100)_2} S(1100)2代表第3位有1,且第2位有1的集合,那 S ( 1100 ) 2 S_{(1100)_2} S(1100)2不就是从 S x S_{x} Sx x & ( 1100 ) 2 = x x \& (1100)_2=x x&(1100)2=x,递推过来的下标集合嘛
  • 这就是经典的 S O S d p SOSdp SOSdp S O S d p SOSdp SOSdp求的是 ∑ m a s k & i = i B [ i ] \sum_{mask\&i=i}B[i] mask&i=iB[i]
  • 而本题求的是: ∑ m a s k & i = m a s k B [ i ] \sum_{mask\&i=mask}B[i] mask&i=maskB[i],状态转移反过来就好
  • d p [ i ] [ j ] dp[i][j] dp[i][j]代表的是有多少个数可以将前 j j j位的某些位置1变为0,从而变为 i i i

AC代码:

#include <bits/stdc++.h>
#define ft first
#define sd second
#define pb push_back
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) //不能跟puts混用
#define seteps(N) fixed << setprecision(N)
#define endl "\n"
const int maxn = 1e6 + 10;
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const ll mod = 1e9 + 7;
int n, a[maxn];
int dp[1 << (21)][22];
void update(int res, int num) {
    if (num > 20) return;
    if (dp[res][num] > 1) return;
    dp[res][num]++;
    update(res, num + 1);
    if ((res >> num) & 1) update(res ^ (1 << num), num);
}
int main() {
    IOS;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    int ans = 0;
    for (int i = n; i >= 1; i--) {
        int res = 0, sta = 0;
        for (int j = 20; j >= 0; j--) {
            if ((a[i] >> j) & 1) res |= (1 << j);
            else if (dp[sta | (1 << j)][20] > 1) res |= (1 << j), sta |= (1 << j);
        }
        update(a[i], 0);
        if (i <= n - 2) ans = max(ans, res);
    }
    cout << ans << endl;
    return 0;
}

参考博客:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值