zoukankan      html  css  js  c++  java
  • 【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^{lchoose 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
    ",1ll*ans*qmi(n-1, MO-2)%MO);
    40     return 0;
    41 }

    END

  • 相关阅读:
    面向对象的三个基本特征(讲解)
    GridView 72般绝技
    Asp.net 将数据库里的记录转换成json
    jquery json asp.net 将各种对象:list ..等转换成
    sql2000 分页存储过程
    .NET中DataSet转化Json工具类
    从攻击者痕迹看内网常见命令
    从攻击者角度看SetMpreference小结
    Java NIO 实现服务端和客户端的通信示例
    spark streaming 监听器执行顺序
  • 原文地址:https://www.cnblogs.com/antiquality/p/11348771.html
Copyright © 2011-2022 走看看