Luogu P5296 [北京省选集训2019]生成树计数

Link

Kirchhoff定理是没办法处理生成树的权值为边权和的情况的,因此考虑将其转化为边权积的形式。

[(sumlimits_{i=1}^na_i)^k=k![x^k]exp((sumlimits_{i=1}^na_i)x)=k![x^k]prodlimits_{i=1}^nexp(a_ix) ]

那么我们将一条边((u,v,w))的边权视作(exp(wx))即可。
因为我们只需要求(x^k)的系数,因此计算过程中可以全程对(x^{k+1})取模。

#include<cctype>
#include<cstdio>
#include<cstring>
using i64=long long;
const int N=37,P=998244353;
int n,k;i64 fac[N],ifac[N],e[N][N],g[N][N][N],ans[N];
void inc(i64&a,i64 b){a+=b-P,a+=a>>63&P;}
void dec(i64&a,i64 b){a-=b,a+=a>>63&P;}
i64 pow(i64 a,i64 b){i64 r=1;for(;b;b>>=1,a=a*a%P)if(b&1)r=r*a%P;return r;}
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
void add(int u,int v)
{
    i64 pw=1,x;
    for(int i=0;i<=k;++i,pw=pw*e[u][v]%P)
	x=pw*ifac[i]%P,dec(g[u][v][i],x),dec(g[v][u][i],x),inc(g[u][u][i],x),inc(g[v][v][i],x);
}
void mul(i64*a,i64*b,i64*c)
{
    static i64 t[N];memset(t,0,8*k+8);
    for(int i=0;i<=k;++i) for(int j=0;i+j<=k;++j) (t[i+j]+=a[i]*b[j])%=P;
    memcpy(c,t,8*k+8);
}
void inv(i64*a,i64*c)
{
    static i64 b[N],t[N];memset(t+1,0,8*k),t[0]=pow(a[0],P-2);
    for(int i=1;i<=k;++i) b[i]=a[i]*t[0]%P;
    for(int i=1;i<=k;++i) for(int j=1;j<=i;++j) (t[i]+=(P-b[j])*t[i-j])%=P;
    memcpy(c,t,8*k+8);
}
int main()
{
    n=read(),k=read(),fac[0]=ifac[0]=1;
    for(int i=1;i<=k;++i) ifac[i]=pow(fac[i]=i*fac[i-1]%P,P-2);
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) e[i][j]=read();
    for(int i=1;i<=n;++i) for(int j=1;j<i;++j) add(i,j);
    ans[0]=1,--n;
    for (int i=1;i<=n;++i)
    {
	static i64 tmp[N];
	mul(g[i][i],ans,ans),inv(g[i][i],tmp);
	for(int j=n;j>=i;--j) mul(g[i][j],tmp,g[i][j]);
	memset(g[i][i],0,8*k+8),g[i][i][0]=1;
	for(int u=n;u^i;--u)
	    for(int v=n;v>=i;--v)
	    {
		mul(g[i][v],g[u][i],tmp);
		for(int j=0;j<=k;++j) dec(g[u][v][j],tmp[j]);
	    }
    }
    printf("%lld",ans[k]*fac[k]%P);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12969305.html