省选模拟题解
\(T1\) 题解
题意:有一张 \(n\) 个点的有标号无向图,分为了 \(k\) 个连通块,第 \(i\) 个连通块的大小是 \(s_i\),每个连通块都是完全图(节点之间两两有边)。要加 \(k-1\) 条边使得图连通,计算所有连边方案的权值和。假设第 \(i\) 个连通块被多加了 \(d_i\) 条边,那么该连边方案的权值为 \(\prod\limits_{i=1}^kd_i!\)
\(sol\):首先把原图看成缩点后的一棵树,假设现在 \(d\) 序列已经确定了对应的权值也就确定了。但是对应了多种树的形态。要求一个确定的 \(d\) 序列对应了多少种树的形态,转化为求 \(prufer\) 序列的个数即:
\[\frac{(k-2)!}{\prod\limits_{i-1}^k(d_i-1)!}
\]
但是原图中第 \(i\) 个连通块连的边有 \(s_i\) 个点可以选择去连所以一个确定的 \(d\) 序列在原图中对应的连边方式为:
\[\frac{(k-2)!}{\prod\limits_{i-1}^k(d_i-1)!}\times\prod\limits_{i=1}^ks_i^{d_i}
\]
贡献为:
\[\frac{(k-2)!}{\prod\limits_{i-1}^k(d_i-1)!}\times\prod\limits_{i=1}^ks_i^{d_i}\times\prod\limits_{i=1}^kd_i!\\
\]
\[=(k-2)!\times\prod\limits_{i=1}^ks_i^{d_i}d_i
\]
然后\((k-2)!\) 是个定值,后面的考虑 \(DP\) ,我们设 \(f_{i,j}\) 表示前 \(i\) 个连通块 \(\sum\limits_{l=1}^i d_l=j\) 的 \(\prod\limits_{i=1}^ks_i^{d_i}d_i\) 这个值。显然 \(f_{1,j}=j\times s_1^j\) 转移也很简单:
\[f_{i,j}=\sum\limits_{t=1}^{j-1}t\times s_i^t\times f_{i-1,j-t}
\]
但是这个是\(O(k^3)\) 的,考虑怎么优化,我们试着把\(f_{i,j}\) 拆一下。
\[f_{i,j-1}=s_i\times f_{i-1,j-2}+2s_i^2\times f_{i-1,j-3}+3s_i^3\times f_{i-1,j-4}……\\
\]
\[f_{i,j}=s_i\times f_{i-1,j-1}+2s_i^2\times f_{i-1,j-2}+3s_i^3\times f_{i-1,j-3}……\\
\]
\[f_{i,j+1}=s_i\times f_{i-1,j}+2s_i^2\times f_{i-1,j-1}+3s_i^3\times f_{i-1,j-2}……\\
\]
我们把 \(f_{i,j-1}\) 乘一个系数 \(s_i\) ,把 \(f_{i,j+1}\) 乘上一个系数 \(\frac{1}{s_i}\) 就会发现一件神奇的事情!
\[s_if_{i,j-1}=0\times f_{i-1,j}+0\times f_{i-1,j-1}+s_i^2\times f_{i-1,j-2}+2s_i^3\times f_{i-1,j-3}……\\
\]
\[f_{i,j}=0\times f_{i-1,j}+s_i\times f_{i-1,j-1}+2s_i^2\times f_{i-1,j-2}+3s_i^3\times f_{i-1,j-3}……\\
\]
\[\frac{1}{s_i}f_{i,j+1}=1\times f_{i-1,j}+2s_i\times f_{i-1,j-1}+3s_i^2\times f_{i-1,j-2}+4s_i^3\times f_{i-1,j-3}……\\
\]
然后我们就可以抵消掉一大堆东西得到:
\[s_if_{i,j-1}+\frac{1}{s_i}f_{i,j+1}=2f_{i,j}+f_{i-1,j}\\
\]
\[f_{i,j+1}=2s_if_{i,j}+s_if_{i-1,j}-s_i^2f_{i,j-1}\\
\]
换一下元
\[f_{i,j}=2s_if_{i,j-1}+s_if_{i-1,j-1}-s_i^2f_{i,j-2}
\]
然后就可以 \(O(k^2)\) 递推了。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=7005,mod=998244353;
inline ll rd()
{
ll f=1;char c;
while(!isdigit(c=getchar())) if(c=='-') f=-1;
ll x=c^48;
while(isdigit(c=getchar())) x=(x<<3)+(x<<1)+(c^48);
return f*x;
}
ll n,k,fac[N]={1},g[N],s[N];
int f[N][N<<1];
int main()
{
n=rd(),k=rd();ll d=2*(k-1);
for(int i=1;i<=k;i++) fac[i]=fac[i-1]*i%mod,s[i]=rd();g[0]=1;
for(int i=1;i<=d;i++) g[i]=g[i-1]*s[1]%mod;
for(int i=1;i<=d;i++) f[1][i]=g[i]*i%mod;
for(int i=2;i<=k;i++)
for(int j=i;j<=d;j++)
f[i][j]=(2*s[i]*f[i][j-1]%mod-s[i]*s[i]%mod*f[i][j-2]%mod+s[i]*f[i-1][j-1]%mod+mod)%mod;
cout<<(1ll*f[k][d]*fac[k-2]%mod+mod)%mod<<endl;
return 0;
}

浙公网安备 33010602011771号