因为是Manchery讲的bfs树的题目,那么就考虑bfs树
因为bfs树不存在返祖边,所以bfs树上的点的深度就是根节点到它的最短路,那么就一层一层DP。
令
fi,j,s
表示DP到第
i
层,总共用了
考虑转移
先选点
- 当
i<k
时, 方案是
(n−j+s−1s)
(DP到这一层剩下
n−j+s
个 点,但是
n
节点不能在小于
k 层选,所以能选的节点有 n−j+s−1 个)。 - 当
i=k
层,
n
节点就必须要选,所以方案数是
(n−j+s−1s−1)
- 当
i<k
时, 方案是
(n−j+s−1s)
(DP到这一层剩下
n−j+s
个 点,但是
n
节点不能在小于
选的这 s 个点要与上一层的
w 个点连边,且这 s 个点都至少要连出一条边,这样总连边的方案数是(2w−1)s 。- 这些点直接相互可以连边,总方案数是 2s×(s−1)2
所以转移是
- i<k→fi,j,s=fi−1,j−s,w×(n−j+s−1s)×(2w−1)s×2s×(s−1)2
- i=k→fi,j,s=fi−1,j−s,w×(n−j+s−1s−1)×(2w−1)s×2s×(s−1)2
这样就好啦
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=110,P=1e9+7;
int n,k,f[N][N][N];
int fac[N],inv[N];
inline int Pow(int x,int y){
int ret=1;
for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;
return ret;
}
inline int C(int x,int y){
return 1LL*fac[x]*inv[y]%P*inv[x-y]%P;
}
inline void add(int &x,int y){
if((x+=y)>=P) x-=P;
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
scanf("%d%d",&n,&k);
fac[0]=inv[0]=inv[1]=1;
for(int i=1;i<=n;i++) fac[i]=1LL*fac[i-1]*i%P;;
for(int i=2;i<=n;i++) inv[i]=1LL*(P-P/i)*inv[P%i]%P;
for(int i=1;i<=n;i++) inv[i]=1LL*inv[i]*inv[i-1]%P;
f[0][1][1]=1;
for(int i=1;i<=k;i++)
for(int j=1;j<=n;j++)
for(int s=1;s<=j;s++)
for(int w=1;w<=j-s;w++)
if(i<k)
add(f[i][j][s],1LL*f[i-1][j-s][w]*C(n-j+s-1,s)%P*Pow(Pow(2,w)-1,s)%P*Pow(2,s*(s-1)/2)%P);
else
add(f[i][j][s],1LL*f[i-1][j-s][w]*C(n-j+s-1,s-1)%P*Pow(Pow(2,w)-1,s)%P*Pow(2,s*(s-1)/2)%P);
int ans=0;
for(int i=k+1;i<=n;i++)
for(int j=1;j<=n;j++)
add(ans,1LL*f[k][i][j]*Pow(2,(n-i)*(n-i-1)/2+j*(n-i))%P);
printf("%d\n",ans);
return 0;
}