每日一题 | 二进制操作问题

时间:2022-07-23
本文章向大家介绍每日一题 | 二进制操作问题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

昨日题解

每日一题 | 灾后重建问题

codeforces联想杯D题,链接:https://codeforces.com/gym/102623/problem/D

给定m条边,让我们将n个点全部连通,这是一个很明显的最小生成树问题。但是有两个棘手的点,第一个点是每条边的长度不确定,是斐波那契数列算出来的。第二个点是我们希望得到的度最小,但最小生成树的解法并不一定唯一

所以整个问题的核心就是分析一下上面这两个问题,只要这两个问题解决了,剩下的都好办了。首先来说第一个问题,虽然斐波那契数列并不难求,只需要递推就可以了,但问题是这里的n的范围比较大,对应的斐波那契数列的值是天文数字,我们是不可能存储得下来的。但是我们分析一下,会发现其实我们没有必要计算出具体的值。因为题目当中的序号是从1开始的,那么我们就可以保证每个点的值都各不相同。

并且斐波那契数列有一个性质是从第三个数开始,每个数等于前面两个数的和。首先,我们可以知道这个数列是递增的,其次,我们知道题目当中的一条边的长度等于连接的两个点的斐波那契数列之和,我们假设一条边连接的点是(a, b),另外一条边是(c, d)。那么我们怎么比较这两条边的大小呢?

其实很简单,我们假设a > b, c > d,如果a > c,那么第一条边大,否则第二条边大。我们做一个极端的假设,假设c = a-1, d = a-2,那么根据斐波那契数列的性质可以得到fib(a) = fib(c) + fib(d),但是由于fib(b)至少大于0,所以fib(a) + fib(b) > fib(c) + fib(d)。

也就是说我们根本没必要求出每条边具体对应的值,直接用序号比较大小就可以。另外我们还可以得到一个结论就是,这个图当中的每一条边的长度都各不相同。既然如此,我们进一步可以得到,对应的最小生成树是唯一的。既然如此就很简单了, 我们跑一下最小生成树,然后计算一下每个点的度数即可。

放上不能AC的代码(Python天然比C++慢,导致过不了本题)

class DisjointSet:

    def __init__(self, element_num=None):
        self._father = [i for i in range(element_num + 2)]
        self._rank = [0 for _ in range(element_num + 2)]

    def _query(self, x):

        if self._father[x] == x:
            return x
        self._father[x] = self._query(self._father[x])
        return self._father[x]

    def merge(self, x, y):
        x = self._query(x)
        y = self._query(y)

        if x == y:
            return

        if self._rank[x] < self._rank[y]:
            self._father[x] = y
        else:
            self._father[y] = x

            if self._rank[x] == self._rank[y]:
                self._rank[x] += 1


    def same(self, x, y):
        return self._query(x) == self._query(y)


n, m = map(int, input().split(' '))

edges = []

for _ in range(m):
    u, v = map(int, input().split(' '))
    l = (u, v) if u > v else (v, u)
    edges.append((u, v, l))


disjoint_set = DisjointSet(n+2)

edges = sorted(edges, key=lambda x: x[2])

degrees = [0 for _ in range(n+2)]

ret = 0

for e in edges:
    u, v = e[0], e[1]
    if disjoint_set.same(u, v):
        continue

    disjoint_set.merge(u, v)
    degrees[u] += 1
    degrees[v] += 1
    ret = max(ret, degrees[u], degrees[v])


print(ret)

今日问题

二进制操作问题

给定两个非负整数的数组,第一个数组A有n个数,第二个数组B有m个数。对于第一个数组A中的每一个数,你需要从B数组当中选一个数来得到,其中,这里的&表示二进制中的与操作。

对于所有的,我们需要计算,这里的|表示的或操作,要求这样得到最小的d。

- END -