[bfs树 分层图][DP] hihocoder Pro.1147 时空阵

该博客主要介绍了如何利用BFS树解决一个与最短路径和动态规划(DP)相关的竞赛题目。通过建立BFS树,博主讨论了在不存在返祖边的情况下,如何进行一层一层的DP。DP状态定义为`fi,j,s`,表示到达第i层使用了j个点,第i层有s个点的方案数。当达到第k层(根节点为0层)时,其余点可以随意连接。转移方程分别考虑了i<k和i=k的情况,结合选择节点的方案数和边的连接方式来完成状态转移。" 115412992,10303690,SQL查询教程:单表查询与条件筛选,"['数据库理论', 'SQL查询', '数据操作']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目传送门

因为是Manchery讲的bfs树的题目,那么就考虑bfs树
因为bfs树不存在返祖边,所以bfs树上的点的深度就是根节点到它的最短路,那么就一层一层DP。
fi,j,s 表示DP到第 i 层,总共用了 j 个点,第 i 层有 s 个点时的方案数,因为题目只要求第 n 个点距离为 k,所以我们只要DP到第 k 层(令1节点为第0层),剩下的点就瞎连边就行了。

考虑转移
fi,j,s 可以从 fi1,js,w 转移。

  • 先选点

    • i<k 时, 方案是 (nj+s1s) (DP到这一层剩下 nj+s 个 点,但是 n 节点不能在小于 k 层选,所以能选的节点有 nj+s1 个)。
    • i=k 层, n 节点就必须要选,所以方案数是(nj+s1s1)
  • 选的这 s 个点要与上一层的 w 个点连边,且这 s 个点都至少要连出一条边,这样总连边的方案数是(2w1)s

  • 这些点直接相互可以连边,总方案数是 2s×(s1)2

所以转移是

  • i<kfi,j,s=fi1,js,w×(nj+s1s)×(2w1)s×2s×(s1)2
  • i=kfi,j,s=fi1,js,w×(nj+s1s1)×(2w1)s×2s×(s1)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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值