zoukankan      html  css  js  c++  java
  • #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_jsum_{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}^nfrac{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
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    web应用/http协议/web框架
    算法-排序-1.冒泡排序/2.选择排序/3.插入排序
    算法-基础和查找-1.汉诺塔/2.顺序查找/3.二分查找/4.顺序查找和二分查找的比较
    前端开发
    前端开发
    前端开发
    前端开发
    数据库
    LeetCode : Add Binary
    LeetCode : Length of Last Word
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/12242807.html
Copyright © 2011-2022 走看看