Codeforces 1208F Bits And Pieces 位运算 + 贪心 + dp

Stella981
• 阅读 489

题意:给你一个序列a, 问a[i] ^ (a[j] & a[k])的最大值,其中i < j < k。

思路:我们考虑对于每个a[i]求出它的最优解。因为是异或运算,所以我们从高位向低位枚举,如果这一位a[i]是0,我们就在a[i]的右边找两个位置让它们按位与起来这位是1。那么,我们贪心的保留可以通过按位与凑出某个二进制数的最靠右的两个位置。这个可以通过dp的方式预处理出来。之后,我们枚举每一个数a[i],先找出它的哪些位是0,之后从高位到低位枚举,判断这一位是否可以变成1。如果之前已经加上的位再加上这一位可以被凑出来,那么我们就加上这一位。在所有的a[i]得出的答案中去最大值即可。

代码:

#include <bits/stdc++.h>
#define pii pair<int, int>
#define INF 0x3f3f3f3f
#define db double 
using namespace std;
const int maxn = 2000010;
pii dp[1 << 21];
int a[maxn];
void update(int mask, int val) {
    if(dp[mask].second == 0) {
        dp[mask].second = val;
        return;
    }
    if(dp[mask].first == val || dp[mask].second == val) return;
    if(val > dp[mask].second) {
        dp[mask].first = dp[mask].second;
        dp[mask].second = val;
    } else if(val > dp[mask].first) {
        dp[mask].first = val;
    }
}
void merge(int mask1, int mask2) {
    if(dp[mask2].first != 0) update(mask1, dp[mask2].first);
    if(dp[mask2].second != 0) update(mask1, dp[mask2].second);
}
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        update(a[i], i);
    }
    for (int i = 0; i < 21; i++) {
        for (int j = 0; j < (1 << 21); j++) {
            if((j >> i) & 1) {
                merge(j ^ (1 << i), j);
            }
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int mask = 0, tmp = (1 << 21) - 1 - a[i];
        for (int j = 20; j >= 0; j--) {
            if((tmp >> j) & 1) {
                if(dp[mask ^ (1 << j)].first > i && dp[mask ^ (1 << j)].second != 0) {
                    mask  ^= (1 << j);
                }
            }
        }
        if(dp[mask].first > i && dp[mask].second != 0) ans = max(ans, a[i] ^ mask);
    }
    printf("%d\n", ans);
}
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
2年前
java 二进制(原码 反码 补码),位运算,移位运算,约瑟夫问题
一.二进制,位运算,移位运算1.二进制对于原码,反码,补码而言,需要注意以下几点:(1).Java中没有无符号数,换言之,Java中的数都是有符号的;(2).二进制的最高位是符号位,0表示正数,1表示负数;(3).正数的原码,反码,补码都一样;(4).负数的反码它的原码符号位不变,其他位取反;(5).
Wesley13 Wesley13
2年前
java中的7个位运算运算符
位运算指的是针对整数的二进制进行的位移操作。位运算提供比算术运算更高的效率,但是位运算的代码可读性较差,建议所有使用位运算的地方写上注释。Java中提供7个位运算符用于位运算。左移(<<)左移运算是将操作数二进制值逐位左移若干位,左移过程中符号位不变,高位溢出则舍弃,低位则补0。范例结果范例结果00000001<<
Wesley13 Wesley13
2年前
OC中的位运算
转载:https://www.jianshu.com/p/b868b30c0c88OC中的位运算和C/C语言的位运算是一样的。一般有&(按位与),|(按位或),~(按位取反),<<(左移),(右移),^(异或)以及&(按位与然后赋值),|(按位或然后赋值)等对枚举类型的操作中常常会见到。例如定义一个季节SeasonT
Wesley13 Wesley13
2年前
C语言位运算
位运算应用口诀清零取反要用与,某位置一可用或若要取反和交换,轻轻松松用异或移位运算要点1它们都是双目运算符,两个运算分量都是整形,结果也是整形。        2"<<"左移:右边空出的位上补0,左边的位将从字头挤掉,其值相当于乘2。       3""右移:右边的位被挤掉。对于左边移出的空位,如果是正数则空
Stella981 Stella981
2年前
20180109Java位运算
一,Java位运算1.表示方法:  在Java语言中,二进制数使用补码表示,最高位为符号位,正数的符号位为0,负数为1。补码的表示需要满足如下要求。 (1)正数的最高位为0,其余各位代表数值本身(二进制数)。 (2)对于负数,通过对该数绝对值的补码按位取反,再对整个数加1。 2.位运算符位运算表达式由
可莉 可莉
2年前
20180109Java位运算
一,Java位运算1.表示方法:  在Java语言中,二进制数使用补码表示,最高位为符号位,正数的符号位为0,负数为1。补码的表示需要满足如下要求。 (1)正数的最高位为0,其余各位代表数值本身(二进制数)。 (2)对于负数,通过对该数绝对值的补码按位取反,再对整个数加1。 2.位运算符位运算表达式由
Wesley13 Wesley13
2年前
C#位运算
在C中可以对整型运算对象按位进行逻辑运算,按位进行逻辑运算的意义是:依次取被运算对象的每个位,进行逻辑运算,每个位的逻辑运算结果是结果值的每个位,C支持的位逻辑运算符如下表。!(https://oscimg.oschina.net/oscnet/e3ff3ca0d8190d7cf6a5c8269feaab32004.jpg)1、位逻辑非运算
Stella981 Stella981
2年前
CodeForces165E 位运算 贪心 + 状压dp
http://codeforces.com/problemset/problem/165/E题意 两个整数 _x_ 和 _y_ 是 兼容的,如果它们的位运算"AND"结果等于0,亦即 _a_ & _b_  0 。例如,数 90 (10110102) 和 36 (1001002) 是兼容的,因为 10110102 & 1001002  0
Stella981 Stella981
2年前
A Mini Locomotive(动态规划 01)
 /\ 题意:选出3个连续的数的个数 为K的区间,使他们的和最大分析: dp\j\\i\max(dp\jk\\i1\value\j\,dp\j1\\i\);dp\j\\i\:从j个数种选出i个连续区间 数值的最大和value\j\:第j个区间内的数的和和背包有点像,但要活用\
Wesley13 Wesley13
2年前
2020CCPC长春站部分题解
F题dsuontree维护一个数组t\\\\\\。t\i\\j\\k\表示当前子树内a\u\i且u的第j位是k的u的个数。!(https://imgblog.csdnimg.cn/20201109122529290.png)这个东西没办法直接维护的,但是对于!j(https://private.cod