位运算符-牛客寒假集训营2-建通道
题目:
题意:
输
入
点
的
数
量
n
输入点的数量n
输入点的数量n
输
入
n
个
点
的
编
号
v
1
,
v
2
,
.
.
.
,
v
n
输入n个点的编号v_1,v_2,...,v_n
输入n个点的编号v1,v2,...,vn
点
v
i
到
v
j
的
代
价
为
v
i
点v_i到v_j的代价为v_i
点vi到vj的代价为vi^
v
j
的
值
在
二
进
制
下
最
低
位
的
1
所
代
表
的
十
进
制
数
v_j的值在二进制下最低位的1所代表的十进制数
vj的值在二进制下最低位的1所代表的十进制数
例
如
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=3。3在二进制下位11,最低为位1在十进制下为1
现
要
使
得
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 根据代价计算的表达式我们发现,对vi和vj两点抑或后再取最低位1的值就是vi和vj相异的最低位所代表的十进制的值。如v1=1时其二进制为01,v2=2时其二进制为10,v1与v2相异的最低位是最后一位,二者抑或后,最后一位为1,lowbit(1^ 2 ) = 01 = 1 。 2)=01=1。 2)=01=1。
也 就 是 说 , 我 们 需 要 在 所 有 点 中 找 到 二 进 制 数 相 异 的 位 置 最 低 的 两 个 点 v i 和 v j , 记 位 第 c 位 , 即 可 计 算 两 点 相 连 通 的 最 低 代 价 。 也就是说,我们需要在所有点中找到二进制数相异的位置最低的两个点v_i和v_j,\\记位第c位,即可计算两点相连通的最低代价。 也就是说,我们需要在所有点中找到二进制数相异的位置最低的两个点vi和vj,记位第c位,即可计算两点相连通的最低代价。
对 于 剩 余 的 点 , 假 设 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)。 对于剩余的点,假设vi的第c位为1,vj的第c位为0,我们将所有第c位为1的点与vj相连,所有第c位为0的点与vi相连这样就能得到最小总代价 2c∗(nd−1)。
代码:
// & : 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所表示的十进制的值。 对于lowbit函数的理解:对a+b=0。如6=00000110,−6=11111001+1=11111010可见最低位的1右边的0,在取反之后就会全部变为1,再加一,进位后,取反获取的1全部进位了,最低位的1又被进位上来了,即最低位的1保持不变。此时将二者进行与运算,即可得到最低位的1所表示的十进制的值。