BZOJ 5093: [Lydsy1711月赛]图的价值

第二类斯特林数模版题

需要一些组合数的小$ trick$

upd:这里更新了本题巧妙的$ O(k)$做法,虽然常数很大就是了


传送门:here

题意:求所有$ n$个节点的无重边自环图的价值和,定义一张图的价值为每个点度数的$ k$次方和,点有标号


$ Solution$

显然每个节点的贡献是独立的

枚举每个节点的度数,和这个点不联通的边可连可不连

$ ans=n*2^{\frac{(n-1)(n-2)}{2}}\ \ \sum\limits_{i=0}^{n-1}i^kC_{n-1}^i$

我们实际要求解的东西就是$ f(n,m)=\sum\limits_{i=0}^ni^mC_n^i$

把$i^m$用斯特林数展开得
$f(n,m)=\sum\limits_{i=0}^n\sum\limits_{j=0}^mC_i^jS(m,j)j!C_n^i$

把$j$移动到前面得
$f(n,m)=\sum\limits_{j=0}^mS(m,j)j!\sum\limits_{i=0}^nC_i^jC_n^i$
考虑后面这个$\sum\limits_{i=0}^nC_i^jC_n^i$是什么
本质相当于在$n$个物品中选出集合$A$,再在集合$A$中选取$j$个物品
也就是在$n$个物品中选取$j$个物品,其他$n-j$个物品可在集合$A$中也可不在
因此$\sum\limits_{i=0}^nC_i^jC_n^i=C_n^j2^{n-j}$
$f(n,m)=\sum\limits_{j=0}^mS(m,j)j!C_n^j2^{n-j}$
$NTT$筛出斯特林数直接计算即可
复杂度$O(k \ log \ k)$


 

$my \ code$

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define p 998244353
#define file(x)freopen(x".in","r",stdin);freopen(x".out","w",stdout)
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x = 0; char zf = 1; char ch = getchar();
    while (ch != '-' && !isdigit(ch)) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int i,j,k,m,n,x,y,z,cnt;
int a[1000010],b[1000010],R[1000010],lim;
ll ksm(ll x,ll y){
    if(!y)return 1;ll ew=1;
    while(y>1){
        if(y&1)y--,ew=x*ew%p;
        y>>=1,x=x*x%p;
    }return x*ew%p;
}
int inv[200010],S[200010];
struct poly{
    int n,m,lim;
    void init(int k){
        //a[i]=(-1)^i / i!    b[i] = i^k/i!
        n=k;
        a[0]=1;b[0]=0;
        for(rt i=1;i<=k;i++){
            a[i]=-1ll*a[i-1]*inv[i]%p;
            b[i]=ksm(i,k)*a[i]%p;
            if(i&1)b[i]=-b[i];
        }
        lim=1;while(lim<=n+n)lim<<=1;
        for(rt i=1;i<lim;i++)R[i]=(R[i>>1]>>1)|(i&1?(lim>>1):0);
    }
    void NTT(int *A,int fla){
        for(rt i=0;i<lim;i++)if(i<R[i])swap(A[i],A[R[i]]);
        for(rt i=1;i<lim;i<<=1){
            ll w=ksm(3,p/2/i);
            for(rt j=0;j<lim;j+=i<<1){
                ll K=1;
                for(rt k=0;k<i;k++,K=K*w%p){
                    ll x=A[j+k],y=K*A[i+j+k];
                    A[j+k]=(x+y)%p;A[i+j+k]=(x-y)%p;
                }
            }
        }
        if(fla==-1){
            reverse(A+1,A+lim);
            for(rt i=0;i<=n;i++)S[i]=1ll*A[i]*ksm(lim,p-2)%p;
        }
        
    }
    void main(int k){
        init(k);
        NTT(a,1);NTT(b,1);
        for(rt i=0;i<lim;i++)a[i]=1ll*a[i]*b[i]%p;
        NTT(a,-1);
    }
}NTT;
int main(){
    n=read()-1;k=read();
    inv[0]=inv[1]=1;
    for(rt i=2;i<=k+1;i++)inv[i]=1ll*inv[p%i]*(p-p/i)%p;

    NTT.main(k);
    ll jc=1,C=1,ans=0,sum=ksm(2,n-j);
    for(rt j=0;j<=k;j++){
        (ans+=S[j]*jc%p*C%p*sum)%=p;
        jc=jc*(j+1)%p;C=C*(n-j)%p*inv[j+1]%p;
        sum=sum*inv[2]%p;
    }
    cout<<(ans*(n+1)%p*ksm(2,(ll)n*(n-1)/2)%p+p)%p;
    return 0;
}

 

posted @ 2018-10-31 12:47  Kananix  阅读(409)  评论(0编辑  收藏  举报

Contact with me