独钓寒江雪

Description

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

注:本质不同是指树的同构不同。

Solution

tag : 树哈希,dp

无根树不好处理,先讨论有根树的情况。

\(dp_{u,0/1}\) 是以 \(u\) 为根的有根树,该节点不选/选的方案数,转移简单。

但是当子树 \(u\) 有两个同构的子树时,若两个子树的独立集相同就会算重。

所以考虑使用树哈希,找到所有同构的子树,分别使用组合数处理,发现类似插板法。

然后钦定重心为根,转换为有根树处理。

当重心为一个点时,答案就是 \(dp_{rt,0} + dp_{rt,1}\)

当重心为一条边(有两个重心)时,分类讨论两个子树是否同构:

  • 若不同构,答案显然为 \(dp_{x,0} \times dp_{y,0} + dp_{x,1} \times dp_{y,0} + dp_{x,0} \times dp_{y,1}\),其中 \(x\)\(y\) 分别为两个重心。

  • 若同构,答案要在上式的基础上还要减去两个子树相同情况,就是 \(dp_{rt,0} \times dp_{rt,1} + \dfrac{dp_{rt,0}^2 + dp_{rt,0}}{2}\)

注意哈希处理得不好容易被卡,我的方法是把素数表随机打乱。

点击查看代码
#include<bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define ull unsigned long long
#define int long long
#define rep(i,l,r) for(int i=(l); i<=(r); ++i)
#define drep(i,r,l) for(int i=(r); i>=(l); --i)
using namespace std;
const int N=5e5+5,M=7368787,mod=1e9+7;
int n,x,y,tot,p[N],sz[N],dp[N][2],inv[N];
bool vis[M+5];
vector<int>rt,G[N];
ull h[N];
inline int C(int x,int y,int res=1) {
	rep(i,x-y+1,x) (res*=i)%=mod;
	rep(i,1,y) (res*=inv[i])%=mod;
	return res;
}
inline void dfs(int x,int fa,int mx=0) {
	sz[x]=1;
	for(auto y:G[x]) if(y^fa) dfs(y,x),sz[x]+=sz[y],mx=max(mx,sz[y]);
	if(max(mx,n-sz[x])<=n/2) rt.push_back(x);
}
inline void dfs2(int x,int fa) {
	unordered_map<int,int>cnt,mp;
	h[x]=sz[x]=dp[x][0]=dp[x][1]=1;
	for(auto y:G[x]) if(y^fa) dfs2(y,x),sz[x]+=sz[y],h[x]+=h[y]*p[sz[y]],cnt[h[y]]++,mp[h[y]]=y;
	for(auto [v,c]:cnt) {
		int y=mp[v];
		dp[x][0]=dp[x][0]*C(dp[y][0]+dp[y][1]+c-1,c)%mod;
		dp[x][1]=dp[x][1]*C(dp[y][0]+c-1,c)%mod;
	}
}
signed main() {
	FASTIO;
	cin>>n,inv[1]=1;
	rep(i,2,n) cin>>x>>y,G[x].push_back(y),G[y].push_back(x);
	rep(i,2,n) inv[i]=mod-mod/i*inv[mod%i]%mod;
	rep(i,2,M) if(!vis[i]) {
		p[++tot]=i;
		rep(j,2,M/i) vis[i*j]=1;
	}
	random_shuffle(p+1,p+tot+1);
	dfs(1,0);
	if(rt.size()==1) dfs2(rt[0],0),cout<<(dp[rt[0]][0]+dp[rt[0]][1])%mod,exit(0);
	else {
		dfs2(rt[0],rt[1]),dfs2(rt[1],rt[0]);
		if(h[rt[0]]==h[rt[1]]) cout<<(dp[rt[0]][0]*dp[rt[0]][1]+C(dp[rt[0]][0]+1,2))%mod;
		else cout<<(dp[rt[0]][0]*dp[rt[1]][0]+dp[rt[0]][1]*dp[rt[1]][0]+dp[rt[0]][0]*dp[rt[1]][1])%mod;
	}
}
posted @ 2025-03-01 11:23  _Double10  阅读(31)  评论(1)    收藏  举报