#2708. 黑暗(dark)

题目描述

n个点的无向图,每条边都可能存在,一个图的权值是连通块个数的m次方,求所有可能的图的权值和,答案对998244353取模。

数据范围

$T \le 1000,n \le 30000,m \le 15$

题解

设 $h_{n,i}$ 表示 $n$ 个点的图有 $i$ 个连通块的方案数,答案可以写成$$\sum_{k=1}^nh_{n,k}k^m$$
化式子,得 $$\sum_{k=1}^nh_{n,k}\sum_{i=1}^m\{_i^m\}(_i^k)i!$$
其中 $\{_i^m\}$ 代表第二类斯特林数。将 $i$ 提前,得$$\sum_{i=1}^m\{_i^m\}i!\sum_{k=1}^nh_{n,k}(_i^k)$$
设 $f_{n,i}=\sum_{k=1}^nh_{n,k}(_i^k)$ , 则上述式子为$$\sum_{i=1}^m\{_i^m\}i!f_{n,i}$$
接着我们化简 $f_{n,i}$ , 可以枚举编号为1的点所在的连通块大小,我们预处理 $g_i$ 表示i个点的图是连通的的方案数,这个就是城市规划那题要求的,我们可以得到$$f_{n,i}=\sum_{k=1}^n(_i^k)\sum_{j=1}^{n-k+1}(_{j-1}^{n-1})g_jh_{n-j,k-1}$$
由组合数的递推式得$$f_{n,i}=\sum_{j=1}^n(_{j-1}^{n-1})g_j\sum_{k=1}^{n-j+1}((_{i-1}^{k-1})+(_i^{k-1}))h_{n-j,k-1}$$
用 $k$ 取代 $k-1$ ,得$$f_{n,i}=\sum_{j=1}^n(_{j-1}^{n-1})g_j(f_{n-j,i-1}+f_{n-j,i})$$
于是我们可以得到$$f_{i,n}=(n-1)!\sum_{j=1}^n\frac{g_j}{(j-1)!}\frac{f_{i-1,n-j}}{(n-j)!}+\frac{g_j}{(j-1)!}\frac{f_{i,n-j}}{(n-j)!}$$
于是我们就可以分治 $Ntt$ 求出 $f_i(x)$ 效率: $O(nlog^2n)$

代码

#include <bits/stdc++.h>
using namespace std;
const int M=16,P=998244353,N=30001,Z=N<<2;
int ans,T,n,m,s[M][M],jc[N],ny[N],g[Z],b[Z],c[Z],d[Z],A[Z],B[Z],f[M][N],t,p,G[2]={3,(P+1)/3},r[Z];
int X(int x){if (x>=P) x-=P;return x;}
int K(int x,int y){
    int A=1;
    for (;y;y>>=1,x=1ll*x*x%P)
        if (y&1) A=1ll*A*x%P;
    return A;
}
void Ntt(int *g,bool o){
    for (int i=0;i<t;i++)
        if (i<r[i]) swap(g[i],g[r[i]]);
    for (int wn,i=1;i<t;i<<=1){
        wn=K(G[o],(P-1)/(i<<1));
        for (int x,y,j=0;j<t;j+=(i<<1))
            for (int w=1,k=0;k<i;k++,w=1ll*w*wn%P)
                x=g[j+k],y=1ll*w*g[i+j+k]%P,
                g[j+k]=X(x+y),g[i+j+k]=X(x-y+P);
    }
    if (o)
        for (int i=0,v=K(t,P-2);i<t;i++)
            g[i]=1ll*v*g[i]%P;
}
void pre(int l){
    for (t=1,p=0;t<l;t<<=1,p++);
    for (int i=0;i<t;i++)
        r[i]=(r[i>>1]>>1)|((i&1)<<(p-1));
}
void getinv(int *a,int *b,int l){
    if (l==1){
        b[0]=K(a[0],P-2);return;
    }
    getinv(a,b,(l+1)>>1);
    for (int i=0;i<l;i++)
        A[i]=a[i],B[i]=b[i];
    pre(l+l);Ntt(A,0);Ntt(B,0);
    for (int i=0;i<t;i++)
        A[i]=1ll*A[i]*B[i]%P*B[i]%P;
    Ntt(A,1);for (int i=0;i<l;i++)
        b[i]=X(X(b[i]+b[i])-A[i]+P);
    for (int i=0;i<t;i++) A[i]=B[i]=0;
}
void solve(int *a,int l,int r){
    if (l==r){
        a[l]=X(a[l]+d[l]);
        if (l) a[l]=1ll*a[l]*jc[l-1]%P;
        return;
    }
    int mid=(l+r)>>1;solve(a,l,mid);
    for (int i=0;i<=mid-l;i++) b[i]=1ll*a[l+i]*ny[l+i]%P;
    for (int i=0;i<r-l;i++) c[i]=1ll*g[i+1]*ny[i]%P;
    pre(mid-l+1+r-l);Ntt(b,0);Ntt(c,0);
    for (int i=0;i<t;i++) b[i]=1ll*b[i]*c[i]%P;
    Ntt(b,1);for (int i=mid+1;i<=r;i++) a[i]=X(a[i]+b[i-l-1]);
    for (int i=0;i<t;i++) b[i]=c[i]=0;
    solve(a,mid+1,r);
}
int main(){
    s[0][0]=1;
    for (int i=1;i<M;i++)
        for (int j=1;j<=i;j++)
            s[i][j]=(s[i-1][j-1]+1ll*s[i-1][j]*j%P)%P;
    jc[0]=1;
    for (int i=1;i<N;i++) jc[i]=1ll*i*jc[i-1]%P;
    ny[N-1]=K(jc[N-1],P-2);
    for (int i=N-1;i;i--) ny[i-1]=1ll*i*ny[i]%P;
    for (int i=0;i<N;i++)
        g[i]=1ll*K(2,(1ll*i*(i-1)/2)%(P-1))*ny[i]%P;
    getinv(g,b,N);
    for (int i=0;i<N;i++)
        g[i]=1ll*K(2,(1ll*i*(i-1)/2)%(P-1))*ny[i-1]%P;
    pre(N+N);Ntt(g,0);Ntt(b,0);
    for (int i=0;i<t;i++) g[i]=1ll*g[i]*b[i]%P,b[i]=0;
    Ntt(g,1);for (int i=1;i<N;i++) g[i]=1ll*g[i]*jc[i-1]%P;
    for (int i=0;i<M;i++){
        if (!i){d[0]=g[0]=1;goto out;}
        for (int j=1;j<N;j++)
            b[j]=1ll*g[j]*ny[j-1]%P,
            c[N-j-1]=1ll*f[i-1][N-j-1]*ny[N-j-1]%P;
        pre(N+N);Ntt(b,0),Ntt(c,0);
        for (int j=0;j<t;j++)
            d[j]=1ll*b[j]*c[j]%P,b[j]=c[j]=0;
        Ntt(d,1);out:;solve(f[i],0,N-1);
    }
    for (scanf("%d",&T);T--;){
        scanf("%d%d",&n,&m);ans=0;
        for (int i=1;i<=m;i++)
            ans=X(ans+1ll*s[m][i]*f[i][n]%P*jc[i]%P);
        printf("%d\n",ans);
    }
    return 0;
}

 

上一篇:用python训练计算机进行自主学习


下一篇:工作记录——6