Codeforces 1097 G. Vladislav and a Great Legend
一道好题。
题意:给定一棵
其中
吐槽:比赛的时候看到这道题想到的是组合数做法,然而是
题解:首先有一种套路叫做幂转下降幂,即这个等式:
考虑它的一个等价形式:
应用到这道题的式子中,我们要求的就是:
考虑交换求和的顺序:
为了方便起见,我们在下文不严谨的称连通一个点集
从组合意义上来讲,后面的式子等价于枚举所有大小为
先考虑如何简化判断虚树的边的过程。首先,假如点集中所有点的
我们设
为了方便,我们不妨在开始时将空集也计算在内,最后在去掉空集的影响,容易得知去掉空集只要在最后将
那么初始时只有
接下来考虑在原来的基础上如何添加一棵以
接下来只要根据乘法原理将
在算完所有子树以后就得到了所有关于
这样做的复杂度看上去是
首先考虑若干个
再考虑
最后是两个
于是最终的复杂度是
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using std::min;
using std::vector;
const int mod=1e9+7;
inline int add(int a,int b)
{
return (a+=b)>=mod?a-mod:a;
}
inline int sub(int a,int b)
{
return (a-=b)<0?a+mod:a;
}
inline int mul(int a,int b)
{
return (long long)a*b%mod;
}
inline int qpow(int a,int b)
{
int res=1;
for(;b;a=mul(a,a),b>>=1)
if(b&1)
res=mul(res,a);
return res;
}
const int N=1e5+5,K=205;
int n,k,ans;
int S[K][K],fact[K];
int size[N];
vector<int> e[N];
int f[N][K];
int tmp[K],tmp2[K];
int g[K];
void dfs(int x,int father)
{
int kx,ky;
register int i,j;
f[x][0]=2;size[x]=1;
for(auto y:e[x])
if(y!=father)
{
dfs(y,x);kx=min(k,size[x]);ky=min(k,size[y]);
memcpy(tmp,f[y],sizeof(int)*(ky+1));
for(i=ky;i>0;i--)
tmp[i]=add(tmp[i],tmp[i-1]);
for(i=0;i<=ky;i++)
g[i]=sub(g[i],tmp[i]);
tmp[0]=add(tmp[0],1);
memset(tmp2,0,sizeof(int)*(k+1));
for(i=0;i<=kx;i++)
for(j=0;j<=ky&&i+j<=k;j++)
tmp2[i+j]=add(tmp2[i+j],mul(f[x][i],tmp[j]));
memcpy(f[x],tmp2,sizeof(int)*(k+1));
size[x]+=size[y];
}
f[x][0]=sub(f[x][0],1);
kx=min(k,size[x]);
for(i=0;i<=kx;i++)
g[i]=add(g[i],f[x][i]);
return;
}
signed main()
{
int x,y;
register int i,j;
scanf("%d%d",&n,&k);
fact[0]=1;
for(i=1;i<=k;i++)
fact[i]=mul(fact[i-1],i);
S[0][0]=1;
for(i=1;i<=k;i++)
{
S[i][0]=0;S[i][i]=1;
for(j=1;j<i;j++)
S[i][j]=add(S[i-1][j-1],mul(j,S[i-1][j]));
}
for(i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1,0);
for(i=1;i<=k;i++)
ans=add(ans,mul(mul(S[k][i],fact[i]),g[i]));
printf("%d\n",ans);
return 0;
}