题意:
给定 n n n个点,要求从 1 1 1到 2 2 2的最短路为 d d d,点之间可以连一条双向边,也可以不连,问合法的图有多少种,答案 m o d p mod\ p mod p
n ≤ 150 n\le150 n≤150
solution:
分层图 d p dp dp
设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示距离 1 1 1点为 i i i,总共选了 j j j个点, i i i这一层选了 k k k个点
考虑转移,要把 2 2 2这个点拿出来,到最后第 d − 1 d-1 d−1到 d d d层的时候再考虑,因此前面要考虑前 d − 1 d-1 d−1层的
首先枚举第 i + 1 i+1 i+1层选 l l l个点,那么选出来这 l l l个点有 C n − j − 1 l C_{n-j-1}^l Cn−j−1l种方案,然后这 l l l个点可以连那 k k k个点也可以不连,但不能全都不连,所以还要乘上 ( 2 k − 1 ) l (2^k-1)^l (2k−1)l,还有这 l l l个点内部可以任意连,所以还要乘 2 l ∗ ( l − 1 ) 2 2^{\frac{l*(l-1)}{2}} 22l∗(l−1)。
算完前 d − 1 d-1 d−1层,单独考虑第 d d d层,首先枚举 i , j i,j i,j表示第 d − 1 d-1 d−1层前共选了 i i i个点,第 d − 1 d-1 d−1层选了 j j j个, 2 2 2这个点可以和 j j j中任意一个点连但不能都不连,同时剩下 n − i − 1 n-i-1 n−i−1个可以和 j j j个点任意连, n − i n-i n−i个点也可以任意互相连。
注意这个复杂度看起来是 n 4 n^4 n4还得带个 l o g log log,但我们可以用预处理去掉 l o g log log,然后通过卡边界来降低复杂度让它可过
附上代码:
#include
#include
#include
#include
#include
#define LL long long
#define maxn 155
using namespace std;
int n,d,mod;
LL bin[maxn*100],fac[maxn],inv[maxn],pw[maxn*100][maxn],ans,f[maxn][maxn][maxn];
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
inline void upd(LL &x,LL y){
x+=y; x>=mod?x-=mod:0;
}
inline LL qpow(LL x,int k){
LL ret=1;
while(k){
if(k&1) (ret*=x)%=mod;
(x*=x)%=mod; k>>=1;
} return ret;
}
inline void prework(){
fac[0]=1; bin[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
for(int i=1;i<=n*n/2;i++) bin[i]=(bin[i-1]<<1)%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n;i;i--) inv[i-1]=inv[i]*i%mod;
for(int i=0;i<=n*n/2;i++){//2^i
LL x=bin[i]-1; pw[i][0]=1;
for(int j=1;j<=n;j++)
pw[i][j]=pw[i][j-1]*x%mod;
}
}
inline LL C(int n,int m){
if(n<m) return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
freopen("distance.in","r",stdin);
freopen("distance.out","w",stdout);
n=rd(); d=rd(); mod=rd(); prework();
f[0][1][1]=1;
for(int i=0;i<d-1;i++)//第i层
for(int j=i+1;j<=n-d+i;j++)//选了j个点
for(int k=1;k<=j-i;k++)//第i层选k个
if(f[i][j][k])
for(int l=1;l<=n-j-d+i+1;l++)//i+1层选l个
upd(f[i+1][j+l][l],(LL)f[i][j][k]*C(n-j-1,l)%mod*pw[k][l]%mod*bin[l*(l-1)>>1]%mod);
for(int i=d;i<n;i++)//第d-1层及以前选了i个点
for(int j=1;j<=i-d+1;j++)//第d-1层选了j个
upd(ans,f[d-1][i][j]*(bin[j]-1+mod)%mod*bin[(n-i-1)*j]%mod*bin[(n-i)*(n-i-1)>>1]%mod);
printf("%lld\n",ans);
return 0;
}