2018.10.24 一题——bfs思想上dp解决图论(看似)问题

题目:

~zZSleepy住在E.Space附近。
但也不是非常近。
E.Space的家和~zZSleepy的家的距离是 D 。
E.Space住在G区。
G区有 n 个互不相同的路口,有些路口之间可能有双向道路相连。
~zZSleepy的家在 1 号路口的拐角处。
E.Space的家在 2 号路口的拐角处。
~zZSleepy想知道,一共有多少种可能的不同的道路连接情况。
定义两个路口的距离为从一个路口走到另一个路口至少需要经过道路的数量。
定义两种道路连接情况不同当且仅当存在两个路口在一种方案中有道路连接,在另一种方案中没有道路连接。

保证 1≤ D < n ≤ 150, 10000 ≤ p ≤ 10^9+20。

保证 p 是质数。

  过了一个多小时想出了式子,但T了一些点;看题解得知快速幂的log可以预处理掉,而且还要卡边界;于是预处理快速幂,又卡了卡边界,即A。

  后来发现题解说预处理的是那个 2^k (我早就预处理过了),而那个地方的快速幂因为 l 是从1开始一个个枚举,所以可以顺便乘好……

  它是基于 bfs 的思想。考虑1、2点间的距离时,考虑从1号点出发,经过的路径是什么样子;用 bfs 的想法,不区分每个点而只记每一层的点数,就可以用 dp 状态来刻画。

  设 dp[ i ][ j ][ k ] 表示与1号点距离为 i 、一共选了 j 个点(即bfs已经涉及了 j 个点)、本层有 k 个点的方案数。(不含2号点)

  之所以记录 k ,是为了转移的时候算方案。

  转移就是枚举下一层有 l 个点,则 l 个点与 k 个点之间连边, l 个点中每个点与 k 个点至少有1条边; l 个点之间可以随便同层连边。所以乘上 C( n-j-1,l ) * ( 2^k - 1 )^l * 2^( l*(l-1)/2 )。

  只做到距离为D-1;D层特殊地把2号点加进来;剩下的点可以随便连,因为前面部分不同,所以随便连也不会重复;因为不关注剩下点的分层情况,所以直接随便连即可(也包括了不连通)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=151,M=N*N;
int n,D,mod,dp[N][N][N],c[N][N],ans,bin[M],pw[M][N];
void upd(int &x){x>=mod?x-=mod:0;}
int Pw(int x,int k)
{
  int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;
}
void init()
{
  for(int i=0;i<=n;i++)c[i][0]=1;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      c[i][j]=c[i-1][j-1]+c[i-1][j],upd(c[i][j]);
  bin[0]=1; int m=n*n>>1;
  for(int i=1;i<=m;i++)bin[i]=((ll)bin[i-1]*2)%mod;
  for(int i=1;i<=m;i++)
    for(int j=1;j<n;j++)pw[i][j]=Pw(bin[i]-1,j);
}
int main()
{
  freopen("distance.in","r",stdin);
  freopen("distance.out","w",stdout);
  scanf("%d%d%d",&n,&D,&mod);
  init();
  dp[0][1][1]=1;
  for(int i=0;i<D-1;i++)
    {
      int lmj=n-1-(D-1-i);
      for(int j=i+1;j<=lmj;j++)
    for(int k=1;k<=j-i;k++)
      if(dp[i][j][k])
        {
          int lml=n-1-j-(D-1-(i+1));
          for(int l=1;l<=lml;l++)
        dp[i+1][j+l][l]=(dp[i+1][j+l][l]+
                 (ll)dp[i][j][k]*c[n-j-1][l]%mod*pw[k][l]%mod*bin[l*(l-1)>>1])%mod;
        }
    }
  for(int j=D;j<n;j++)
    {
      int lmk=j-D+1;
      for(int k=1;k<=lmk;k++)
    {
      ans=(ans+(ll)dp[D-1][j][k]*(bin[k]-1)%mod*bin[(n-j-1)*(k+1)]%mod*bin[(n-j-1)*(n-j-2)>>1])%mod;
    }
    }
  printf("%d\n",ans);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/9846234.html