位运算 - 状态压缩 - Compatible Numbers - CodeForces - 165E

位运算 - 状态压缩 - Compatible Numbers - CodeForces - 165E

题意:

给 定 一 个 长 度 为 n 的 序 列 , 对 序 列 中 的 每 一 个 数 a i , 在 序 列 中 查 询 是 否 存 在 a j 使 得 a i & a j = 0 , 对 每 个 a i , 输 出 一 个 对 应 a j 即 可 , 若 不 存 在 , 则 输 出 − 1 。 给定一个长度为n的序列,对序列中的每一个数a_i,在序列中查询是否存在a_j使得a_i\&a_j=0,\\对每个a_i,输出一个对应a_j即可,若不存在,则输出-1。 naiaj使ai&aj=0aiaj1

Examples:

Input:
2
90 36

Output:
36 90


Input:
4
3 6 3 6

Output:
-1 -1 -1 -1


Input:
5
10 6 9 8 2

Output:
-1 8 2 2 8

数据范围:

1 < = n < − 1 0 6 , 1 < = a i < = 4 × 1 0 6 . T i m e   l i m i t : 4000 m s , M e m o r y   l i m i t : 262144 k B 1<=n<-10^6,1<=a_i<=4×10^6.\\Time\ limit:4000 ms,Memory\ limit:262144 kB 1<=n<1061<=ai<=4×106.Time limit4000msMemory limit262144kB


分析:

根 据 & 运 算 的 性 质 , 对 于 一 个 二 进 制 数 a , 要 使 得 a & b = 0 , 那 么 仅 需 将 a 的 1 都 和 0 相 与 , a 的 0 对 应 的 位 置 可 任 意 取 。 根据\&运算的性质,对于一个二进制数a,要使得a\&b=0,那么仅需将a的1都和0相与,a的0对应的位置可任意取。 &a使a&b=0a10a0

比 如 a = 10100 , 则 b = 0 × 0 × × 。 比如a=10100,则b=0×0××。 a=10100b=0×0××

用 一 个 数 组 f , 对 输 入 的 元 素 对 应 的 元 素 做 一 个 映 射 。 用一个数组f,对输入的元素对应的元素做一个映射。 f

我 们 可 以 先 做 最 极 端 的 情 况 , 把 所 有 的 1 变 成 0 , 0 变 成 1 , 又 因 为 原 本 是 0 的 位 置 可 以 取 任 意 值 , 原 本 是 1 的 位 置 必 须 变 0 , 这 样 就 会 有 部 分 合 法 状 态 被 遗 漏 , 是 因 为 原 本 是 0 的 位 置 可 能 仍 然 是 0 , 但 是 我 们 都 取 1 了 , 然 后 我 们 从 大 到 小 开 始 查 找 所 有 可 能 的 状 态 , 对 每 个 状 态 , 我 们 将 其 的 某 个 零 位 置 1 , 置 1 后 若 能 得 到 之 前 已 经 被 标 记 过 的 状 态 , 说 明 该 状 态 与 之 前 的 状 态 对 应 的 是 同 一 个 元 素 , 将 该 状 态 也 映 射 到 对 应 元 素 。 我们可以先做最极端的情况,把所有的1变成0,0变成1,\\又因为原本是0的位置可以取任意值,原本是1的位置必须变0,\\这样就会有部分合法状态被遗漏,是因为原本是0的位置可能仍然是0,但是我们都取1了,\\然后我们从大到小开始查找所有可能的状态,对每个状态,我们将其的某个零位置1,\\置1后若能得到之前已经被标记过的状态,说明该状态与之前的状态对应的是同一个元素,将该状态也映射到对应元素。 100101000111

注意: 对 于 元 素 a i , 将 a i 与 ( 1 < < 23 ) − 1 异 或 , 因 为 a i < ( 1 < < 23 ) − 1 , 故 结 果 必 然 是 大 于 a i 的 。 因 此 , 再 枚 举 与 a i 对 应 的 其 他 状 态 时 , 需 要 从 大 到 小 枚 举 。 对于元素a_i,将a_i与(1<<23)-1异或,因为a_i<(1<<23)-1,故结果必然是大于a_i的。\\\qquad因此,再枚举与a_i对应的其他状态时,需要从大到小枚举。 aiai(1<<23)1ai<(1<<23)1aiai

具体落实:

① 、 用 一 个 全 1 的 二 进 制 数 与 a i 异 或 , 将 a i 的 所 有 位 上 的 数 取 反 。 ①、用一个全1的二进制数与a_i异或,将a_i的所有位上的数取反。 1aiai

② 、 从 大 到 小 枚 举 其 他 可 能 与 a i 对 应 的 状 态 , 将 其 也 映 射 到 a i 。 ②、从大到小枚举其他可能与a_i对应的状态,将其也映射到a_i。 aiai

③ 、 对 每 个 a i , 查 询 数 组 f , 若 f [ a i ] 存 在 , 说 明 a i 对 应 的 a j 存 在 在 序 列 当 中 , 输 出 即 可 。 ③、对每个a_i,查询数组f,若f[a_i]存在,说明a_i对应的a_j存在在序列当中,输出即可。 aiff[ai]aiaj

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>

using namespace std;

const int N=1e6+10;

int n,a[N],f[(1<<23)];

int main()
{
    cin>>n;
    for(int i=0;i<n;i++) 
    {
        scanf("%d",&a[i]);
        f[a[i]^(1<<23)-1]=a[i];
    }
    
    for(int i=(1<<23)-1;i;i--)
        if(!f[i])
            for(int j=0;j<23;j++)
                if(f[i|(1<<j)])
                    {f[i]=f[i|(1<<j)];break;}
                    
    for(int i=0;i<n;i++)
        if(f[a[i]])
            printf("%d ",f[a[i]]);
        else printf("-1 ");
        
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值