Not Too Close 题解
题号太长就不放在题目里了,给个链接。
题意简述
一张无向图有 $n(2\le n\le30)$ 个点,已知它的所有边权都是 $1$。求满足点 $1$ 和点 $2$ 之间的最短路为给定的 $D(1\le D<n)$ 的无向图的数量。
题目分析
考虑在每个图上将到点 $1$ 的距离相同的点看作同一层。那么我们得到每一层的点只会与上一层、下一层及同层的点有连边(连边的两个点跨度不会超过 $1$ 层,否则距离点 $1$ 更远的那个点到 $1$ 的最短路可以更短,矛盾)。由于边是无向边,不妨只在距离点 $1$ 更近的那个点统计跨层的边。
设 $f_{i,j,k}$ 为共 $j$ 个与点 $1$ 距离不超过 $i$ 的点,且共 $k$ 个点与点 $1$ 的距离正好为 $i$ 的方案数(多记 $k$ 一维是为了方便扩展状态)。对于每个状态的扩展,枚举下一层点的个数 $l$。则方案数可分为三部分:
- 下一层的点具体是哪些:$\binom{n-j-1}{l-[i+1=D]}$
- 下一层内点互相之间的连边情况:$2^{\binom l2}$
- 该层与下一层之间连边情况:$(2^k-1)^l$
回到原问题,我们只计算前 $D$ 层的 $f$。而对于每一个 $f_{D,j,k}$,未确定的点互相之间可以任意连,但都只能连第 $D$ 层确定的点,因此方案数为 $2^{k(n-j)+\binom{n-j}2}$。
总时间复杂度为 $O(n^4)$,$n\le30$ 绰绰有余。
代码实现
#include<bits/stdc++.h>
using namespace std;
int n,d,mod,pow2[45010],f[160][160][160],C[160][160],ans;
int main()
{
scanf("%d%d%d",&n,&d,&mod);
pow2[0]=1;
for(int i=1;i<=n*n;i++)
pow2[i]=(pow2[i-1]<<1)%mod;//预处理 2 的幂
for(int i=0;i<=n;i++)
C[i][0]=C[i][i]=1;
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;//组合数
f[0][1][1]=1;
for(int i=0;i<d;i++)
for(int j=i;j<=n;j++)
for(int k=1;k<=j-i+1;k++)
if(f[i][j][k])
for(int tmp=f[i][j][k],l=1;l<=n-j-d+i+1;l++)
tmp=1ll*tmp*(pow2[k]-1)%mod,f[i+1][j+l][l]=(f[i+1][j+l][l]+1ll*tmp*pow2[l*(l-1)/2]%mod*C[n-j-1][l-(i+1==d)]%mod)%mod;//上文提到的三种情况乘法原理乘在一起就行了
for(int i=d;i<=n;i++)
for(int k=1;k<=n;k++)
ans=(ans+1ll*f[d][i][k]*pow2[(n-i)*(n-i-1)/2+(n-i)*k]%mod)%mod;//第 D 层统计答案
printf("%d",ans);
return 0;
}