CF959E Mahmoud and Ehab and the xor-MST

原题链接

题目大意

\(n\)个点的完全图标号\((0\sim n-1)\),\(i\)\(j\)连边权值为\(i\bigoplus j\),求\(\rm{MST}\)的值

题解

挺有意思的一道题,但网上好像没有证明?

我来证一发。

首先,利用\(\rm{Kruskal}\)的思想我们想到先选最小边。

那显然是\(1\)

我们不难发现边权为一的边都要选,因为打过Dinic的都知道\(0\bigoplus 1 = 1, 1\bigoplus 1 = 0, 2\bigoplus 1 = 3, 3\bigoplus 1 = 2, \cdots\)(因为\(a\bigoplus b = c\implies a\bigoplus c = b\),所以反一下是一样的)。我们发现\(0\)只与\(1\)联通,\(2\)只与\(3\)联通,\(2n\)只与\(2n+1\)联通。

我们发现联通块少了一半(确切来说是从\(n\)个连通块变成了\(\lceil\frac{n}{2}\rceil\)块),连了\(\lfloor\frac{n}{2}\rfloor\)条边。

我们考虑继续连边。我们发现如果有点对\((2n, 2n+1)\)与点对\((2m, 2m+1)\)连边:

1101010 1101011
1011100 1011101

比如上面这个例子,出最后一位外前面几位已经确定了,唯有最后一位(因为异或没有进位,而\(2n\)\(2n+1\)只有最后一位不同)。显然\(1\)\(0\)劣,我们考虑尽量往\(0\)选。我们发现两个点对都有\(0\)\(1\)(一奇一偶嘛),所以一定可以凑成\(0\)(虽然最后一块可能只有一个数,但由于其他块都有两个,所以仍能凑成)。所以我们不妨把所有点对的全部除\(2\),最终统计答案是乘\(2\)即可,因为一位最后一位肯定是\(0\)

这样递归下去就可以了。

代码并不长:

#include <iostream>

using namespace std;

typedef long long LL;

inline LL sol(LL n)
{
    if(n == 1LL)
        return 0LL;
    else
        return (sol((n + 1) >> 1) << 1) + (n >> 1);
}

int main()
{
    LL n;
    cin >> n;
    cout << sol(n);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/pfypfy/p/9471357.html