题目链接:https://ac.nowcoder.com/acm/contest/3003/I
题目很简单,我们来分析一下目标:
1.求两个点的权值的异或,一个^就能解决
2.求一个数二进制最低位1的位置.
首先,我们要建立的是一个最小生成树(跟最小生成树其实没关系),这些数据在位运算时有一些规律
1.两个相同的数lowbit()结果为0,所以如果有两个点的权值相同我们一定会让他们两个相连,由此,我们可以先对权值进行排序去重,去重后的权值个数是我们后面要计算的权值,那些相同的权值,因为我们要把它们连起来,并且他们的代价是0所以可以忽略.
2.下面再看如何使我们的lowbit整体代价最低,我们非常希望两个数异或之后的数二进制最右面那一位就是1,这样的代价当然是最小的.所以我们想,只要有一个数,他的二进制最右面那一位与其他的不同,我们就可以得到想要的结果(我们可以把所有的数分成两部分,最右面是0的,最右面是1的,如果两部分都有数,那么最后的结果就是将两部分的数互相配对就可以,答案为1*去重之后的剩下的点数),但是,有可能所有的数都是偶数,那么最右面那一位就都是0,我们就需要考虑右数第二位.
由此,如何寻找这些数中,最低的 存在不同数字 的那一位 就成了关键
我们这么做
int v1 = 0,v0 = 0xffffffff;//v1记录哪一位存在1,v0记录哪一位存在0
for (int i = 1;i <= n;i++)//n是去重后的点的数量
{v1 |=v[i]//v[i]存储点的权值
v0 &= v[i]
}
这样得到的结果,v0中是0的位,说明剩下的数中,有这一位是0的数,v1中是1的位代表剩下的这些数中,有这一位是1的数
只需要将lowbit(v0^v1),就可以得到最低位1的位置.
这里,lowbit也有一个小技巧
int lowbit(int x)
{return x &(-x)
}
这样就可以得到一个数最低位的1了,注意,得到的数是十进制中这一位代表的数.