zoukankan      html  css  js  c++  java
  • BZOJ5093图的价值(斯特林数)

    题目描述

    “简单无向图”是指无重边、无自环的无向图(不一定连通)。
    一个带标号的图的价值定义为每个点度数的k次方的和。
    给定n和k,请计算所有n个点的带标号的简单无向图的价值之和。
    因为答案很大,请对998244353取模输出。
    题解
    因为懒得敲公式了,所以就直接粘题解了。
    我们发现在这张图中每个点都是等价的,所以我们就只需要考虑一个点的贡献,最后乘上n就可以了。
    当一个点的度数为i时,我们可以从其他n-1个点中任意挑出i个点和它连边,而其余n-1个点之间可以任意连边。
    然后我们发现后面那一坨和i无关,可以放到前面去,所以我们实际上是在求
    然后ik这种东西有一个恒等式

    S(k,i)为第二类斯特林数,意义为k个小球放入i个不同的盒子里的方案数。

    等式的左边意义为把k个不同小球放到x个不同的盒子中的方案数。

    右边是在枚举有哪些盒子里有球,还是比较好理解的。

    那么我们把这个指数的东西代换完后式子变成了

    把枚举j的sigma提前

    后面的那个东西看起开很难受,如果我们可以把n和j放在一起,式子就可以往前放了。

    从n个小球中选i个,再从i个中选j个等价于从n个小球中选j个,再从剩下的(n-j)个中选(i-j)个。

    于是我们就可以吧C(n,j)提前了,后面的组合数可以直接用恒等式换掉。

    然后我们只要求出所有S(k,j)就可以了,这个用NTT解决。

    代码

    #include<iostream>
    #include<cstdio>
    #define N 2000009
    using namespace std;
    typedef long long ll;
    const int G=3;
    const int Gi=332748118;
    const int mod=998244353; 
    ll l,L,a[N],b[N],jie[N],ni[N],nii[N],n,k,ans,c[N];
    int rev[N];
    ll power(ll x,ll y){
        if(y<0)return 0;
        ll ans=1;x%=mod;
        while(y){if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1;}
        ans=(ans+mod)%mod;
        return ans;
    }
    inline ll ny(ll x){return power(x,mod-2);}
    inline ll C(ll n,ll m){return jie[n]*ni[m]%mod*ni[n-m]%mod;}
    inline void NTT(ll *a,int tag){
        for(int i=0;i<l;++i)if(i>rev[i])swap(a[i],a[rev[i]]);
        for(int i=1;i<l;i<<=1){
            ll wn=power(tag==1?G:Gi,(mod-1)/(i<<1));
            for(int j=0;j<l;j+=(i<<1)){
              ll w=1;
              for(int k=0;k<i;++k,w=w*wn%mod){
                   ll x=a[j+k],y=a[i+j+k]*w%mod;
                   a[j+k]=(x+y)%mod;a[i+j+k]=(x-y+mod)%mod;
              }
            }
       }
    }
    int main(){
    //    cout<<power(3,mod-2);
        scanf("%lld%lld",&n,&k);
        ll yu=n%mod*power(2,(n-1)*(n-2)/2)%mod;n--;
        jie[0]=1;
        for(int i=1;i<=k;++i)jie[i]=jie[i-1]*i%mod;ni[k]=power(jie[k],mod-2);
        for(int i=k-1;i>=0;--i)ni[i]=ni[i+1]*(i+1)%mod;
        for(int i=0;i<=k;++i)a[i]=(power(-1,i)*ni[i]+mod)%mod;
        for(int i=0;i<=k;++i)b[i]=power(i,k)*ni[i]%mod;
        l=1;L=0;
        while(l<=(k<<1))l<<=1,L++; 
        for(int i=1;i<l;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
        NTT(a,1);NTT(b,1);
        for(int i=0;i<l;++i)a[i]=a[i]*b[i]%mod;
        NTT(a,-1);ll nn=ny(l);
        c[0]=1;
        for(int i=1;i<=min(k,n);++i)c[i]=c[i-1]*ny(i)%mod*(n-i+1)%mod;
        for(int i=0;i<=k;++i){
          a[i]=a[i]*nn%mod;
          (ans+=a[i]*jie[i]%mod*c[i]%mod*power(2,n-i)%mod)%=mod;
        }
        ans=ans*yu%mod;
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    课堂练习-电梯调度
    团队开发项目———来用————用户调研报告
    购书思想课堂作业4.14
    针对《来用》的NABC分析
    《梦断代码》读书笔记3
    《梦断代码》读书笔记2
    《大道至简》阅读笔记2
    《大道至简》阅读笔记1
    课堂练习之找出所有的“1”
    典型用户与场景分析
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10256947.html
Copyright © 2011-2022 走看看