题解:luogu P4895(独钓寒江雪)
1. Description
给定一棵树,询问这棵树有多少种本质不同的独立集。
我们称两个独立集本质相同,当我们可以对树进行重标号,使得边一一对应,且选择的点也一一对应。
2. Solution
我们发现,可以将树的重心作为根来统计,会省去很多讨论,这是很直觉的,也是很显然的。
此时考虑一个树形 dp,定义 \(f_{u,0/1}\) 表示 \(u\) 的子树中,\(u\) 未选择/选择的本质不同的独立集方案数。
会计算重复,只会在 \(u\) 的若干子树同构时出现这种情况。
因此,在计算 \(f_{u,0/1}\) 时,利用树哈希判断树同构,将所有同构的子树取出,记数量为 \(m\),选择子树根的方案数是 \(a\),不选择子树根的方案数是 \(b\),由于子树同构,所以他们 \(f_{v,0/1}\) 的值显然是相同的。
如果 \(u\) 选择,那么子树根就一定不选择,所以就相当于将完全相同的 \(m\) 个球染成 \(b\) 种颜色,询问方案数。
如果 \(u\) 不选择,那么子树根就可选可不选,所以就相当于将完全相同的 \(m\) 个球染成 \(a+b\) 种颜色,询问方案数。
很显然,可以利用插板法求解,将 \(m\) 个球染成 \(x\) 种颜色的方案数是 \(C_{m+x-1}^{x-1}=C_{m+x-1}^{m}\),直接计算下降幂,预处理阶乘逆元即可在 \(O(m)\) 的时间复杂度内计算组合数。
最后可以得到总时间复杂度为 \(O(n)\) 的算法。
注意存在两个重心时的讨论,如果两个重心对应的子树同构,那么答案就是 \(f_{x,0}\times f_{x,1}+C_{f_{x,0}+1}^{2}\),否则答案是 \(f_{x,0}\times f_{y,0}+f_{x,1}\times f_{y,0}+f_{x,0}\times f_{y,1}\)。
3. Code
/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int N=5e5+5,mod=1e9+7;
int n;
int prime[N],siz[N],f[N],g[N][2];
int fac[N],inv[N];
ull hsh[N];
vector<int>e[N];
int add(int x,int y){
x+=y;
return x>=mod?x-mod:x;
}
int sub(int x,int y){
x-=y;
return x<0?x+mod:x;
}
int mul(int x,int y){
long long res=1ll*x*y;
return res>=mod?res%mod:res;
}
int binpow(int a,int b){
int res=1;
while(b){
if(b&1)res=mul(res,a);
b>>=1;
a=mul(a,a);
}
return res;
}
namespace TreeHash{
const int M=75e5+5;
bool flag[M];
void init(){
int cnt=0;
for(int i=2;i<=7500000;i++){
if(!flag[i])
prime[++cnt]=i;
if(cnt==n)break;
for(int j=1;j<=cnt&&prime[j]*i<=7500000;j++){
flag[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}
}
void init(int n){
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=mul(fac[i-1],i);
inv[n]=binpow(fac[n],mod-2);
for(int i=n-1;i>=0;i--)
inv[i]=mul(inv[i+1],i+1);
}
void findrt(int u,int fa){
siz[u]=1;
for(int v:e[u]){
if(v==fa)continue;
findrt(v,u);
tomax(f[u],siz[v]);
siz[u]+=siz[v];
}
tomax(f[u],n-siz[u]);
}
bool cmp(int x,int y){
return hsh[x]<hsh[y];
}
int C(int m,int n){
int res=1;
for(int i=1,x=n;i<=m;i++){
res=mul(res,x);
x=sub(x,1);
}
return mul(res,inv[m]);
}
void dfs(int u,int fa){
siz[u]=1;
for(int v:e[u]){
if(v==fa)continue;
dfs(v,u);
}
vector<int>son;
hsh[u]=1;
for(int v:e[u]){
if(v==fa)continue;
son.push_back(v);
siz[u]+=siz[v];
hsh[u]+=hsh[v]*prime[siz[v]];
}
hsh[u]*=siz[u];
sort(son.begin(),son.end(),cmp);
g[u][0]=g[u][1]=1;
int cnt=son.size();
for(int i=0;i<cnt;){
int j=i;
while(j+1<cnt&&hsh[son[i]]==hsh[son[j+1]])
j++;
int tmp=j-i+1,v=son[i];
g[u][0]=mul(g[u][0],C(tmp,add(add(g[v][0],g[v][1]),tmp-1)));
g[u][1]=mul(g[u][1],C(tmp,add(g[v][0],tmp-1)));
i=j+1;
}
}
signed main(){
read(n);
for(int i=2,u,v;i<=n;i++){
read(u),read(v);
e[u].push_back(v);
e[v].push_back(u);
}
TreeHash::init();
init(n);
findrt(1,0);
int mi=n+1;
for(int i=1;i<=n;i++)
tomin(mi,f[i]);
int cnt=0,ans;
for(int i=1;i<=n;i++)
if(f[i]==mi)
cnt++;
if(cnt==1){
int rt=-1;
for(int i=1;i<=n;i++)
if(f[i]==mi)rt=i;
dfs(rt,0);
ans=add(g[rt][0],g[rt][1]);
}else{
int rt1=-1,rt2=-1;
for(int i=1;i<=n;i++)
if(f[i]==mi){
if(rt1==-1)rt1=i;
else rt2=i;
}
dfs(rt1,rt2);
dfs(rt2,rt1);
if(hsh[rt1]==hsh[rt2])
ans=add(mul(g[rt1][0],g[rt1][1]),add(C(2,g[rt1][0]),g[rt1][0]));
else
ans=add(add(mul(g[rt1][0],g[rt2][1]),mul(g[rt1][1],g[rt2][0])),mul(g[rt1][0],g[rt2][0]));
}
write(ans);
}

浙公网安备 33010602011771号