题目大意:给定一棵树,求本质不同的独立集个数对1000000007取模后的值
首先独立集个数应该都会求吧- -
令f[x][0]为x这个点不选的独立集个数
f[x][1]为x这个点选的独立集个数
那么有f[x][0]=Σf[son[x]][0]+f[son[x]][1]
f[x][1]=Σf[son[x]][0]
但是现在要求本质不同
说到本质不同我们很容易想到群论 但是群论显然写不了- -
于是我们考虑对树进行一下处理
首先将树有根化
为了保证形态相同的子树在有根化之后形态依然相同,我们需要找到这棵树的重心
如果重心是两个点,就在这两个点之间添加一个点作为根
如果随便选择一个点作为根,后果就是形态相同的部分有根化之后形态不同- -
比如说过不去2 1 2这组样例- -
而如果选择了重心,由于两个形态相同的部分大小一定相同,故重心一定不在其中之一
接下来考虑对DP方程进行一些处理
比如说有三棵形态完全相同的树,每棵都有ABCD四种方案
那么我选AAB和选BAA是等价的
n个点,涂上m种颜色,那么本质不同的方案数为C(n+m-1,n)
现在的问题就是计算形态相同的子树的个数
我们可以设计一个Hash函数对每棵子树进行哈希
Hash函数越奇葩越好- - 太简单的会有BUG
比如我的方法:
首先对点设一个初值,将子树按照哈希值排序,每次执行:
for(i=1;i<=top;i++)
(((hash[x]*=BASE)+=hash[stack[i]])^=hash[stack[i]])+=hash[stack[i]];
最好自行设计hash函数,但是必须保证形态相同的子树被Hash成相同的值,形态不同的子树被Hash成不同的值
然后就搞过了- - 随便写了一发RANK1了什么情况- - 一定是自然溢出比较快的缘故- -
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 500500
#define MOD 1000000007
#define ORIGIN 233
#define BASE 233333333
using namespace std;
struct abcd{
int to,next;
}table[M<<1];
int head[M],tot=1;
int n,root,cgs[2],size[M];
unsigned long long hash[M];
long long inv[M],f[M][2],ans;
bool Compare(int x,int y)
{
return hash[x] < hash[y];
}
void Add(int x,int y)
{
table[++tot].to=y;
table[tot].next=head[x];
head[x]=tot;
}
void DFS(int x,int from)
{
int i,flag=1;
size[x]=1;
for(i=head[x];i;i=table[i].next)
{
if(table[i].to==from)
continue;
DFS(table[i].to,x);
size[x]+=size[table[i].to];
if(size[table[i].to]<<1>n)
flag=0;
}
if(n-size[x]<<1>n)
flag=0;
if(flag)
(cgs[0]?cgs[1]:cgs[0])=x;
}
long long C(long long n,long long m)
{
int i;
long long re=1;
for(n%=MOD,i=1;i<=m;i++)
(re*=(n-i+1)*inv[i]%MOD)%=MOD;
return re;
}
void Tree_DP(int x,int from)
{
static int stack[M];
int i,j,top=0;
hash[x]=ORIGIN;
for(i=head[x];i;i=table[i].next)
if(table[i].to!=from)
Tree_DP(table[i].to,x);
for(i=head[x];i;i=table[i].next)
if(table[i].to!=from)
stack[++top]=table[i].to;
sort(stack+1,stack+top+1,Compare);
f[x][0]=f[x][1]=1;
for(i=1;i<=top;i=j)
{
for(j=i+1;j<=top&&hash[stack[i]]==hash[stack[j]];j++);
(f[x][0]*=C(j-i+f[stack[i]][0]+f[stack[i]][1]-1,j-i) )%=MOD;
(f[x][1]*=C(j-i+f[stack[i]][0]-1,j-i) )%=MOD;
}
for(i=1;i<=top;i++)
(((hash[x]*=BASE)+=hash[stack[i]])^=hash[stack[i]])+=hash[stack[i]];
}
void Linear_Shaker()
{
int i;
inv[1]=1;
for(i=2;i<=n;i++)
inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
}
int main()
{
int i,x,y;
cin>>n;
Linear_Shaker();
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
Add(x,y);Add(y,x);
}
DFS(1,0);
if(cgs[1])
{
for(i=head[cgs[0]];i;i=table[i].next)
if(table[i].to==cgs[1])
{
table[i].to=table[i^1].to=root=n+1;
break;
}
Add(n+1,cgs[0]);
Add(n+1,cgs[1]);
}
else
root=cgs[0];
Tree_DP(root,0);
if(!cgs[1])
ans=(f[root][0]+f[root][1])%MOD;
else
{
x=cgs[0];y=cgs[1];
if(hash[x]!=hash[y])
ans=(f[x][0]*f[y][0]%MOD+f[x][1]*f[y][0]%MOD+f[x][0]*f[y][1]%MOD)%MOD;
else
ans=(f[x][0]*f[y][1]+C(f[x][0]+1,2) )%MOD;
}
cout<<ans<<endl;
return 0;
}