和 CF1657E 的做法十分相似
题目链接:G - Farthest City
题目大意:问有多少个 \(n(3\le n\le 500)\) 个点的无向连通图满足,若设 \(1\) 到 \(i\) 的最短路距离为 \(dis_i\),则 \(dis_n\) 严格大于其它所有的 \(dis_i\)。输出方案数对 \(M(10^8\le M \le 10^9)\) 取模。
考虑最短路的值一定是连续的,那么若最短路为 \(i\) 的点数有 \(c_i\) 个,且 \(dis_n=k\),则方案数可以计算
\[Ans=\binom{n-2}{c_2,c_3,...,c_{k-1}}\times \prod_{i=1}^{k} (2^{c_{i-1}}-1)^{c_i}\times 2^{\binom{c_i}{2}} \]其中,\(\sum_{i=0}^{k}c_i=n,c_0=c_k=1\)。\((2^{c_{i-1}}-1)^{c_i}\) 的含义为最短路为 \(i\) 的点是从最短路为 \(i-1\) 的点连接过来的,那么对于每个这样的点,他可以选择上一层的任意一个非空子集进行连接。\(2^{\binom{c_i}{2}}\) 的含义则是,对当前层的所有点,两两之间可以任意连接,对应可连接的边数为 \(\binom{c_i}{2}\),于是会有 \(2^{\binom{c_i}{2}}\) 的系数。
考虑背包,按最短路的值从小到大一批批安排当前点,那么有一个初始的 DP 想法,就是令 \(f_{i,j,k}\) 表示当前已经安排了 \(i\) 个点,目前最短路的最大值为 \(j\),对应点的个数为 \(k\) 的方案数,那么枚举最短路值为 \(j+1\) 的点的数量 \(cnt\),可以转移到 \(f_{i+cnt,j+1,cnt}\),转移系数为 \(\binom{n-1-i}{cnt}\times (2^{j}-1)^{cnt}\times 2^{\binom{cnt}{2}}\)。但是直接这样做是 \(O(n^4)\) 的。
发现转移系数与 \(j\) 无关,所以这一维可以直接省去,于是 DP 方式就变成,设 \(f_{i,j}\) 表示当前已经安排了 \(i\) 个点,且目前最短路最大的点数为 \(j\) 的方案数,那么同样枚举下一层的点数 \(k\),就能转移到 \(f_{i+k,k}\),转移系数为 \(\binom{n-1-i}{k}\times (2^{j}-1)^{k}\times 2^{\binom{k}{2}}\)。初始值设 \(f_{1,1}=1\),最后答案即为 \(\sum_{j=1}^{n-1} f_{n-1,j}\times (2^j-1)\)。
#include<bits/stdc++.h>
using namespace std;
#define N 510
#define LL long long
int n,MOD,f[N][N],c[N][N],p[N*N],ans;
LL qow(LL x,LL y){return y?(y&1?x*qow(x,y-1)%MOD:qow(x*x%MOD,y/2)):1;}
int main()
{
scanf("%d%d",&n,&MOD);
p[0]=c[0][0]=1;
for(int i=1;i<=n;i++){
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
}
for(int i=1;i<=n*n;i++)p[i]=2ll*p[i-1]%MOD;
f[1][1]=1;
for(int i=1;i<n;i++)
for(int j=1;j<=i;j++)if(f[i][j])
for(int k=1;i+k<n;k++)
f[i+k][k]=(f[i+k][k]+1ll*f[i][j]*c[n-i-1][k]%MOD*qow(p[j]+MOD-1,k)%MOD*p[k*(k-1)/2]%MOD)%MOD;
for(int j=1;j<=n-1;j++)
ans=(ans+1ll*f[n-1][j]*(p[j]+MOD-1)%MOD)%MOD;
printf("%d\n",ans);
}
时间复杂度为 \(O(n^3 \log n)\),预处理了 \(2^i\) 的值稍微进行了点常数优化。实际上是可以通过 \(O(n^2)\) 预处理每个 \((2^j-1)^k\) 的值把复杂度降到 \(O(n^3)\) 的,但是既然能过就不优化了。╮(╯▽╰)╭