[北京省选集训2019]生成树计数
\((\sum_{i=1}^nw_i)^k\)直观理解一下就是
\[\sum_{\sum c_i=k}w_i^{c_i}
\]
对于某种\(\{c_i\}\),其出现次数就是\(\frac{k!}{\prod c_i!}\)
于是上面的式子就是
\[k!\sum_{c}\frac{w_i^{c_i}}{c_i!}
\]
于是可以套路地构造一个\(\rm EGF\),第\(i\)条边的\(\rm EGF\)就是\(\sum_{i=0}^\infty \frac{w_i^i}{i!}x^i=e^{w_ix}\)
于是我们要求的应该是\(k!\sum_T[k]\prod e^{w_ix}\),不难发现这是一个矩阵树定理的形式,构造一个基尔霍夫矩阵消元求行列式就好了,唯一特别的就是行列式里面变成了多项式
我们可以\(O(k^2)\)的实现多项式乘法和乘法逆,于是复杂度就是\(O(n^3k^2)\)常数相当小
代码
#include<bits/stdc++.h>
#define re register
const int mod=998244353;
inline int dqm(int x) {return x<0?x+mod:x;}
inline int qm(int x) {return x>=mod?x-mod:x;}
struct Poly {int f[31];}a[31][31];
int n,k,fac[33],ifac[33],w[33][33];
inline int ksm(int a,int b) {
int S=1;for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)S=1ll*S*a%mod;return S;
}
inline Poly operator+(const Poly &A,const Poly &B) {
Poly C;for(re int i=0;i<=k;i++) C.f[i]=qm(A.f[i]+B.f[i]);return C;
}
inline Poly operator-(const Poly &A,const Poly &B) {
Poly C;for(re int i=0;i<=k;i++) C.f[i]=dqm(A.f[i]-B.f[i]);return C;
}
inline Poly operator*(const Poly &A,const Poly &B) {
Poly C;
for(re int i=0;i<=k;i++) {
C.f[i]=0;
for(re int j=0;j<=i;j++) C.f[i]=qm(C.f[i]+1ll*A.f[j]*B.f[i-j]%mod);
}
return C;
}
inline Poly INV(const Poly &A) {
Poly C;for(re int i=0;i<=k;i++) C.f[i]=0;
int Inv=ksm(A.f[0],mod-2);C.f[0]=Inv;
for(re int i=1;i<=k;i++) {
for(re int j=1;j<=i;j++) C.f[i]=dqm(C.f[i]-1ll*A.f[j]*C.f[i-j]%mod);
C.f[i]=1ll*C.f[i]*Inv%mod;
}
return C;
}
inline Poly get(int v) {
Poly C;int nw=1;
for(re int i=0;i<=k;i++) C.f[i]=1ll*nw*ifac[i]%mod,nw=1ll*nw*v%mod;
return C;
}
int main() {
scanf("%d%d",&n,&k);if(!k) return printf("%d\n",n>1?ksm(n,n-2):1),0;
for(re int i=1;i<=n;i++)
for(re int j=1;j<=n;j++) scanf("%d",&w[i][j]);
fac[0]=ifac[0]=1;for(re int i=1;i<=k;i++) fac[i]=1ll*fac[i-1]*i%mod;
ifac[k]=ksm(fac[k],mod-2);for(re int i=k-1;i;i--) ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
for(re int i=1;i<=n;i++)
for(re int j=1;j<i;++j) {
Poly t=get(w[i][j]);
a[i][i]=a[i][i]+t;a[j][j]=a[j][j]+t;
a[i][j]=a[i][j]-t;a[j][i]=a[j][i]-t;
}
int o=0;
for(re int i=1;i<n;i++) {
int p=i;
for(re int j=i;j<n;++j) if(a[i][j].f[0]) {p=j;break;}
if(p!=i) std::swap(a[i],a[p]),o^=1;
Poly Inv=INV(a[i][i]);
for(re int j=i+1;j<n;j++) {
Poly t=Inv*a[j][i];
for(re int k=i;k<n;++k)
a[j][k]=a[j][k]-(t*a[i][k]);
}
}
Poly ans;for(re int i=1;i<=k;i++) ans.f[i]=0;ans.f[0]=1;
for(re int i=1;i<n;i++) ans=ans*a[i][i];
if(!o) printf("%d\n",1ll*fac[k]*ans.f[k]%mod);
else printf("%d\n",1ll*dqm(-fac[k])*ans.f[k]%mod);
return 0;
}