BZOJ3162 独钓寒江雪【无根树同构问题】

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20181220_xiang_m_y/article/details/102626820

题目描述:

给定一棵无根树,求其中本质不同的独立集的个数。
n<=500000

题目分析:

如果任选一点作根,那么它选1的情况和它选0的情况有可能是本质相同的。
但如果选重心作根(如果有两个重心,就新建一个点连向两个重心,原来的边断掉),那么它选1的情况和它选0的情况一定是本质不同的。
我们不难看出重心是树中唯一确定的点,没有任何一点能够与它等价。

所以用重心作根, f [ u ] [ 0 / 1 ] f[u][0/1] 表示子树方案数,子树按照哈希值排序,dp即可。
对于同构的 k k 个儿子,如果他们的方案为 p p ,要让它们不重复,就把方案编号然后由小到大取,可以看做把 k k 个球放入 p p 个盒子中,盒子可以为空的方案数,答案是 ( p + k 1 p 1 ) = ( p + k 1 k ) \binom {p+k-1}{p-1}=\binom {p+k-1}{k} 。由于k<mod,所以由lucas可知组合数的上半部分是可以模的。

Code:

#include<bits/stdc++.h>
#define maxn 500005
using namespace std;
typedef unsigned long long ULL;
const int mod = 1e9+7;
const ULL Seed = 19260817;
int n,rt[3],inv[maxn],f[maxn][2],son[maxn],H[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
int getroot(int u,int ff){
	int siz=1,tmp; bool flg=1;
	for(int i=fir[u];i;i=nxt[i]) if(to[i]!=ff){
		siz+=(tmp=getroot(to[i],u));
		if(tmp<<1>n) flg=0;
	}
	if((n-siz)<<1>n) flg=0;
	if(flg) rt[++rt[0]]=u;
	return siz;
}
bool cmp(int i,int j){return H[i]<H[j];}
inline int C(int n,int m){
	int ret=1;
	while(m) ret=1ll*ret*(n--)%mod*inv[m--]%mod;
	return ret;
}
void dfs(int u,int ff){
	f[u][0]=f[u][1]=1;
	for(int i=fir[u];i;i=nxt[i]) if(to[i]!=ff) dfs(to[i],u);
	int cnt=0;
	for(int i=fir[u];i;i=nxt[i]) if(to[i]!=ff) son[++cnt]=to[i];
	sort(son+1,son+1+cnt,cmp);
	H[u]=23333;
	for(int i=1,j,v;i<=cnt;i=j){
		for(v=son[j=i];j<=cnt&&H[son[j]]==H[v];j++) H[u]=H[u]*Seed^H[son[j]];
		f[u][0]=1ll*f[u][0]*C(f[v][0]+f[v][1]+j-i-1,j-i)%mod;
		f[u][1]=1ll*f[u][1]*C(f[v][0]+j-i-1,j-i)%mod;
	}
	H[u]*=Seed*Seed;
}
int main()
{
	int x,y,r;
	scanf("%d",&n);
	for(int i=1;i<n;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
	inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	getroot(1,0);
	if(rt[0]>1){
		r=n+1;
		for(int i=fir[rt[1]];i;i=nxt[i]) if(to[i]==rt[2]) {to[i]=r;break;}
		for(int i=fir[rt[2]];i;i=nxt[i]) if(to[i]==rt[1]) {to[i]=r;break;}
		line(r,rt[1]),line(r,rt[2]);
	}
	else r=rt[1];
	dfs(r,0);
	if(rt[0]>1) printf("%d\n",(H[rt[1]]==H[rt[2]]?C(f[rt[1]][0]+1,2)+1ll*f[rt[1]][1]*f[rt[1]][0]:f[r][0]-1ll*f[rt[1]][1]*f[rt[2]][1]%mod+mod)%mod);
	else printf("%d\n",(f[r][0]+f[r][1])%mod);
}

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/102626820
今日推荐