【bfs分层图 dp】hihocoder#1147 : 时空阵

最短路径树上分层dp的一类套路吧

题目大意

幽香这几天学习了魔法,准备建造一个大型的时空传送阵。

幽香现在可以在幻想乡的n个地点建造一些传送门,如果她建造了从地点a与地点b之间的传送门,那么从a到b和从b到a都只需要单位1的时间。

同时这些地点之间在地理上是非常遥远的,因此来往他们必须使用传送门。

现在幽香想要问你,有多少种建造传送门的方案,使得地点1和地点n之间的最短距离恰好为k?两个方案不同当且仅当建造的传送门的集合不同。不能建造节点到自身的传送门,两个点之间也最多造一个传送门。

$n,k \le 100$


题目分析

特殊性质在于每条边长度都为1,这点让人联想到bfs;而看到最短路则自然想起最短路径树。

那么我们就考虑按照bfs序dp这张图的最短路径树,也就是分层往下dp。在分层dp的树中,跨层的点不能连边;邻层及同层的点可以随意连边,最终目标是把$n$号点安排在$k+1$层,如果还有剩下的点则接下去随意安排。

不过这里有一种省去分类讨论的小trick:我们不计标号地计算剩下$n-1$个点的方案数,而由于这$n-1$个点是完全等价的,相当于最后再把总方案数乘以$(n-1)^{-1}$即可。

用$f_{i,t,l}$表示构造了$i$层,总共使用了$t$个节点(包括1号点),当前层有$l$个节点的方案数。转移时枚举上一层有$p$个节点。边界条件$f_{1,1,1}=1$

大致形状如上所示。

考虑p到l的转移:首先从$n-(t-l)$个点中取出$l$个点,取出的每个点向$p$个点连边有$2^p-1$种方案;$l$个点同层连边有$2^{l\choose 2}$种方案。

最后再枚举所有情况统计一趟即可。

 

 1 #include<bits/stdc++.h>
 2 #define MO 1000000007
 3 typedef long long ll;
 4 const int maxn = 103;
 5 
 6 int n,k,fac[maxn],facinv[maxn],mi[maxn];
 7 ll f[maxn][maxn][maxn],ans;
 8 
 9 int qmi(ll a, ll b)
10 {
11     int ret = 1;
12     for (a%=MO; b; b>>=1,a=1ll*a*a%MO)
13         if (b&1) ret = 1ll*ret*a%MO;
14     return ret;
15 }
16 int C(int n, int m)
17 {
18     if (n < m) return 0;
19     return 1ll*fac[n]*facinv[n-m]%MO*facinv[m]%MO;
20 }
21 int main()
22 {
23     scanf("%d%d",&n,&k);
24     facinv[0] = facinv[1] = fac[0] = mi[0] = 1;
25     for (int i=2; i<=100; i++)
26         facinv[i] = MO-1ll*MO/i*facinv[MO%i]%MO;
27     for (int i=1; i<=100; i++)
28         mi[i] = 2ll*mi[i-1]%MO, fac[i] = 1ll*fac[i-1]*i%MO, facinv[i] = 1ll*facinv[i-1]*facinv[i]%MO;
29     f[1][1][1] = 1;
30     for (int i=2; i<=k+1; i++)
31         for (int t=i; t<=n; t++)
32             for (int l=1; l<=t-i+1; l++)
33                 for (int p=1; p<=t-l-i+2; p++)
34                     f[i][t][l] = (f[i][t][l]+1ll*f[i-1][t-l][p]*qmi(mi[p]-1, l)%MO*C(n-t+l, l)%MO*qmi(2, C(l, 2))%MO)%MO; 
35     for (int i=1; i<=n; i++)
36         for (int j=1; j<=n; j++)
37             if (f[k+1][i][j])
38                 ans = (ans+1ll*f[k+1][i][j]*j%MO*qmi(2, C(n-i, 2))%MO*qmi(2, 1ll*j*(n-i)%MO))%MO;
39     printf("%lld\n",1ll*ans*qmi(n-1, MO-2)%MO);
40     return 0;
41 }

 

 

 

 

END

posted @ 2019-08-13 21:38  AntiQuality  阅读(494)  评论(0编辑  收藏  举报