题意:
n个顶点的完全图,编号为0~n-1,任意两点间的边权为两个点的编号的异或,求最小生成树的边权总和。(仍是普通加和,不是异或和)
思路:
为讨论方便,把输入的n减一。
先写个prim找规律,发现除了0外,每个点 \(i\) 都向 \(i \oplus lowbit(i)\) 连一条边。总和就是 \(ans=\sum\limits_{i=1}^n lowbit(i)\)。
设 \(f(x)\) 表示 \(lowbit(y)=x\) 的 \(y\) 的个数,则 \(ans=\sum\limits _{x=1}^n xf(x)\) 。注意到 \(x\) 只能取 2 的幂,所以 \(ans=\sum\limits _{i=0}^{\lfloor logn\rfloor}2^if(2^i)\) 。
那么 \(f(x)\) 怎么算呢?找规律:\(f(4)=|\{4,12,20,28,\cdots(不能超过n)\}|\),公差为8。事实上 \(f(x)=\lfloor\frac{n-x}{2x}\rfloor+1\)
ll n, ans = 0; cin >> n; n--;
for(ll x = 1; x <= n; x *= 2)
ans += x * ((n-x) / (2*x) + 1);
cout << ans;
还可以dp,直接嗯找规律:
每个顶点的边:\(1,2,1,4,1,2,1,8,1,2,1,4,\cdots\) ,发现每次把当前数组复制一遍,然后在中间插一个 \(2^i\) 。长度每次乘2加1。即有 \(ans(1)=dp[1]=1\),\(ans(2^{i}-1)=dp[i]=2*dp[i-1]+2^{i-1}\) 。
\(ans(x)=ans(msb(x)) + ans(x\oplus msb(x))\) ,\(msb(x)\) 表示不超过x的最大的2的幂。加号右边递归下去,最终如果 \(n\) 的第 \(i\) 位为1,答案就加上 \(ans(2^i)=ans(2^i-1+1)=ans(2^i-1)+2^i=dp[i]+2^i\)。即第 \(i\) 位的贡献。
ll n, ans = 0; cin >> n; n--;
dp[1] = 1; for(int i = 2; i < 40; i++) dp[i] = dp[i-1]*2 + (1ll<<(i-1));
for(int i = 0; i < 40; i++)
if((n>>i)&1) ans += dp[i] + (1ll << i);
cout << ans;