位运算符-牛客寒假集训营2-建通道

位运算符-牛客寒假集训营2-建通道

题目:
在这里插入图片描述

题意:
输 入 点 的 数 量 n 输入点的数量n n
输 入 n 个 点 的 编 号 v 1 , v 2 , . . . , v n 输入n个点的编号v_1,v_2,...,v_n nv1,v2,...,vn
点 v i 到 v j 的 代 价 为 v i 点v_i到v_j的代价为v_i vivjvi^ v j 的 值 在 二 进 制 下 最 低 位 的 1 所 代 表 的 十 进 制 数 v_j的值在二进制下最低位的1所代表的十进制数 vj1
例 如 v 1 = 1 , v 2 = 2 , 那 么 1 例如v1=1,v2=2,那么1 v1=1,v2=2,1^ 2 = 3 。 3 在 二 进 制 下 位 11 , 最 低 为 位 1 在 十 进 制 下 为 1 2=3。3在二进制下位11,最低为位1在十进制下为1 2=3311,11
现 要 使 得 n 个 点 互 相 联 通 , 求 最 小 代 价 现要使得n个点互相联通,求最小代价 使n

题解:
显 然 , 当 v 1 = v 2 时 , v 1 显然,当v1=v2时,v1 v1=v2,v1^ v 2 = 0 , 因 此 我 们 先 对 编 号 相 同 的 点 进 行 去 重 , 记 去 重 后 点 的 数 量 为 n d 。 v2=0,因此我们先对编号相同的点进行去重,\\记去重后点的数量为nd。 v2=0,,nd

根 据 代 价 计 算 的 表 达 式 我 们 发 现 , 对 v i 和 v j 两 点 抑 或 后 再 取 最 低 位 1 的 值 就 是 v i 和 v j 相 异 的 最 低 位 所 代 表 的 十 进 制 的 值 。 如 v 1 = 1 时 其 二 进 制 为 01 , v 2 = 2 时 其 二 进 制 为 10 , v 1 与 v 2 相 异 的 最 低 位 是 最 后 一 位 , 二 者 抑 或 后 , 最 后 一 位 为 1 , l o w b i t ( 1 根据代价计算的表达式我们发现,对v_i和v_j两点抑或后再取最低位1的值\\就是v_i和v_j相异的最低位所代表的十进制的值。如v_1=1时其二进制为01,\\v_2=2时其二进制为10,v_1与v_2相异的最低位是最后一位,二者抑或后,\\最后一位为1,lowbit(1 vivj1vivjv1=101,v2=210v1v21lowbit(1^ 2 ) = 01 = 1 。 2)=01=1。 2)=01=1

也 就 是 说 , 我 们 需 要 在 所 有 点 中 找 到 二 进 制 数 相 异 的 位 置 最 低 的 两 个 点 v i 和 v j , 记 位 第 c 位 , 即 可 计 算 两 点 相 连 通 的 最 低 代 价 。 也就是说,我们需要在所有点中找到二进制数相异的位置最低的两个点v_i和v_j,\\记位第c位,即可计算两点相连通的最低代价。 vivjc

对 于 剩 余 的 点 , 假 设 v i 的 第 c 位 为 1 , v j 的 第 c 位 为 0 , 我 们 将 所 有 第 c 位 为 1 的 点 与 v j 相 连 , 所 有 第 c 位 为 0 的 点 与 v i 相 连 这 样 就 能 得 到 最 小 总 代 价   2 c ∗ ( n d − 1 ) 。 对于剩余的点,假设v_i的第c位为1,v_j的第c位为0,\\我们将所有第c位为1的点与v_j相连,所有第c位为0的点与v_i相连\\这样就能得到最小总代价\ 2^c*(nd-1)。 vic1vjc0c1vjc0vi 2c(nd1)

代码:

// & : 1&1 = 1 ,1&0 = 0 , 0&0 = 0;遇零则零
// | : 1|1 = 1 , 1|0 = 1; 0|0 = 0;遇一则一
// ^ : 1^1 = 0 , 1^0 = 1 ,0^0 = 0;相同则零,不同则一
//
#include<bits/stdc++.h>
#define ll long long
#define inf 0xffffffff
using namespace std;
const int maxn=2e5+5;
int lowbit(int x)               //原理:计算机数据存储通过补码存储,由于负数补码的特殊取反
{                               // 再进位的特性正负数按位与后得到的正式改数二进制表示最后
    return x & (-x);            //一位”1“的位权。
}

int n;
int v[maxn];

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d",&v[i]);
    sort(v+1,v+n+1);
    int nd=unique(v+1,v+n+1)-(v+1);//unique使用前需要排序,返回值是去重后数组的尾地址,nd即元素个数
    int c1=0;//标记1的位置
    int c0=inf;//标记0的位置
    for(int i=1;i<=nd;i++)//标记每一个0和1的位置
    {
        c1|=v[i];
        c0&=v[i];
    }

    int k=c1^c0; //二进制下的k,1: 每一个既有0又有1的位置

    ll ans=0;
    
    //找最低位1的朴素写法
    /*for(int i=0;k>0;i++)//找到既有0又有1的位置的最低位,也就是k的最低位1
    {
        int c=1<<i;//c=2^i
        if(k&c)
        {
            ans=c*(nd-1);//ans=2^i * (m-1) ,c是既有0又有1的位置的最低位
            break;
        }
    }*/
    
    ans=lowbit(k)*(nd-1);
    cout<<ans<<endl;
    return 0;
}

对 于 l o w b i t 函 数 的 理 解 : 对 a + b = 0 。 如 6 = 00000110 , − 6 = 11111001 + 1 = 11111010 可 见 最 低 位 的 1 右 边 的 0 , 在 取 反 之 后 就 会 全 部 变 为 1 , 再 加 一 , 进 位 后 , 取 反 获 取 的 1 全 部 进 位 了 , 最 低 位 的 1 又 被 进 位 上 来 了 , 即 最 低 位 的 1 保 持 不 变 。 此 时 将 二 者 进 行 与 运 算 , 即 可 得 到 最 低 位 的 1 所 表 示 的 十 进 制 的 值 。 对于lowbit函数的理解:对a+b=0。\\如6=0000 0110,-6 = 1111 1001 + 1 = 1111 1010 \\可见最低位的1右边的0,在取反之后就会全部变为1,再加一,进位后,\\取反获取的1全部进位了,最低位的1又被进位上来了,即最低位的1保持不变。\\此时将二者进行与运算,即可得到最低位的1所表示的十进制的值。 lowbita+b=06=000001106=11111001+1=111110101011111

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值