题目大意
给你两个整数 n n n和 m m m,求一个包含 n n n个点的简单无向连通图的个数:
- 对于所有点 u = 2 , … , n − 1 u=2,\dots,n-1 u=2,…,n−1, u u u到 n n n的距离严格小于 1 1 1到 n n n的距离
输出答案模 m m m后的值。
题解
设 d i d_i di表示 1 1 1到 i i i的最短距离,则当 i i i和 j j j之间有边时,若 d i ≤ d j d_i\leq d_j di≤dj,则一定满足 d j − d i ≤ 1 d_j-d_i\leq1 dj−di≤1。我们可以使用分层图 D P DP DP来解决。
设 f i , j f_{i,j} fi,j表示当前用了 i i i个点,最后一层有 j j j个点的方案数,则转移式为
f i , j = ∑ l = 1 i − j f i − j , l × ( 2 l − 1 ) j × 2 j ( j − 1 ) 2 × C n − i + j − 1 j f_{i,j}=\sum\limits_{l=1}^{i-j}f_{i-j,l}\times(2^l-1)^j\times2^{\frac{j(j-1)}{2}}\times C_{n-i+j-1}^j fi,j=l=1∑i−jfi−j,l×(2l−1)j×22j(j−1)×Cn−i+j−1j
其中 l l l表示上一层的点的个数。
首先这一层的每一个点都可以和上一层的点连接。因为上一层有 l l l个点,每个点可以连或不连,再减去所有点都不连的情况,每个点有 ( 2 l − 1 ) (2^l-1) (2l−1)种情况能与上一层的点连通,那么 j j j个点就有 ( 2 l − 1 ) j (2^l-1)^j (2l−1)j种情况。
对于这一层,任意两个点之间都可以连边或不连边,也就是有 2 j ( j − 1 ) 2 2^{\frac{j(j-1)}{2}} 22j(j−1)种情况。
当然,每一个点是不同的,所以对于这一层,我们是从剩下的 ( n − i + j − 1 ) (n-i+j-1) (n−i+j−1)个点(不包括 n n n,所以要减 1 1 1)中选 j j j个点,因此还要乘上 C n − i + j − 1 j C_{n-i+j-1}^j Cn−i+j−1j。
注意最后 n n n是独立的一层,要特判。
由此得到转移式。枚举状态 O ( n 2 ) O(n^2) O(n2),每次转移 O ( n ) O(n) O(n),总时间复杂度为 O ( n 3 ) O(n^3) O(n3)。注意在求 a b a^b ab这类数的时候要提前预处理,不要使用快速幂,否则会 T L E TLE TLE。
code
#include<bits/stdc++.h>
using namespace std;
int n;
long long mod,ans,tw[250005],yh[505][505],v[505][505],f[505][505];
int main()
{
scanf("%d%lld",&n,&mod);
for(int i=0;i<=n;i++){
yh[i][0]=yh[i][i]=1;
for(int j=1;j<i;j++){
yh[i][j]=(yh[i-1][j]+yh[i-1][j-1])%mod;
}
}
tw[0]=1;
for(int i=1;i<=n*n;i++) tw[i]=tw[i-1]*2%mod;
for(int i=0;i<=n;i++){
v[i][0]=1;
long long tmp=(tw[i]-1+mod)%mod;
for(int j=1;j<=n;j++){
v[i][j]=v[i][j-1]*tmp%mod;
}
}
f[1][1]=1;
for(int i=2;i<=n-1;i++){
for(int j=1;j<i;j++){
for(int l=1;l<=i-j;l++){
f[i][j]=(f[i][j]+f[i-j][l]*v[l][j]%mod*tw[j*(j-1)/2]%mod*yh[n-i+j-1][j]%mod)%mod;
}
}
}
for(int i=1;i<n-1;i++){
ans=(ans+f[n-1][i]*(tw[i]-1)%mod)%mod;
}
printf("%lld",ans);
return 0;
}
参考博客:https://blog.youkuaiyun.com/weixin_46700592/article/details/128273233