51Nod1601 完全图的最小生成树计数 Trie Pruffer编码

Posted zhouzhendong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51Nod1601 完全图的最小生成树计数 Trie Pruffer编码相关的知识,希望对你有一定的参考价值。

原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1601.html

题目传送门 - 51Nod1601

题意

技术分享图片

题解

  首先我们考虑如何求答案。

  我们将所有数字按照二进制位从高到低建到 Trie 上,按照 kruscal 思想,我们要保证先选较小的边。

  于是我们很容易得出结论:在 Trie 上,设 $f(x) =$ 合并子树 $x$ 的所有叶子节点的代价,设 $L(x),R(x)$ 分别为 $x$ 的左右子树编号,则 $f(x)=f(L(x))+f(R(x))+Connect(L(x),R(x))$ 。其中 $Connect(a,b)$ 表示在 $a$ 的叶子节点中 和 $b$ 的叶子节点中各选择一个节点,并将他们相连,需要的最小代价。

  这个显然非常容易求。

  最后我们还有一个问题,就是当递归到 Trie 的叶子节点之后,我们发现它们代表的数字全部相同,连任意一条边的代价为 $0$ ,求把它们连成一棵树的方案,就相当于有 $k$ 个点的无根树计数。有一个东西叫做 pruffer 编码,通过这个东西可以得到 $k$ 个点的互不相同的带标号无根树个数为 $k^{k-2}$ 。

  于是问题就解决了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005,S=N*30,mod=1e9+7;
LL read(){
	LL x=0,f=1;
	char ch=getchar();
	while (!isdigit(ch)&&ch!=‘-‘)
		ch=getchar();
	if (ch==‘-‘)
		f=-1,ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
	return x*f;
}
int Next[S][2],tot[S],depth[S],flag[S],cnt=1;
int Pow(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=1LL*x*x%mod)
		if (y&1)
			ans=1LL*ans*x%mod;
	return ans;
}
LL ans1=0,ans2=1;
void build(int v){
	int x=1,t;
	for (int i=29;i>=0;i--){
		t=(v>>i)&1;
		if (!Next[x][t])
			Next[x][t]=++cnt;
		x=Next[x][t];
		depth[x]=i,flag[x]=t;
	}
	tot[x]++;
}
int mindif,situ;
void Min_Cost_Merge(int x,int y,int dif){
	dif|=(flag[x]^flag[y])<<depth[x];
	int f=0;
	for (int k=0;k<2;k++){
		for (int t=0;t<2;t++)
			if (Next[x][t]>0&&Next[y][t^k]>0)
				Min_Cost_Merge(Next[x][t],Next[y][t^k],dif),f=1;
		if (f)
			return;
	}
	if (dif<mindif)
		mindif=dif,situ=0;
	if (dif==mindif)
		situ=(1LL*tot[x]*tot[y]+situ)%mod;
}
int solve(int x){
	if (!x)
		return 0;
	int s=solve(Next[x][0])+solve(Next[x][1]);
	if (s==0&&tot[x]>1)
		ans2=1LL*ans2*Pow(tot[x],tot[x]-2)%mod;
	if (s==2){
		mindif=1<<30,situ=1;
		Min_Cost_Merge(Next[x][0],Next[x][1],0);
		ans1+=mindif,ans2=1LL*ans2*situ%mod;
	}
	return 1;
}
int main(){
	memset(tot,0,sizeof tot);
	memset(Next,0,sizeof Next);
	for (int i=1,n=read();i<=n;i++)
		build(read());
	solve(1);
	printf("%lld
%lld",ans1,ans2);
	return 0;
}

  

以上是关于51Nod1601 完全图的最小生成树计数 Trie Pruffer编码的主要内容,如果未能解决你的问题,请参考以下文章

51nod1601完全图MST计数

51nod--1212 无向图最小生成树

51nod 1212 无向图最小生成树

(图论)51NOD 1212 无向图最小生成树

51Nod-1212 无向图最小生成树

51Nod 1640 天气晴朗的魔法(最小生成树)