zoukankan      html  css  js  c++  java
  • bzoj 5093 图的价值 —— 第二类斯特林数+NTT

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5093

    每个点都是等价的,从点的贡献来看,得到式子:

    ( ans = n * sumlimits_{d=0}^{n-1} d^{k} * 2^{C_{n-1}^{2}} * C_{n-1}^{d} )

    使用 ( n^{k} = sumlimits_{i=0}^{k} S(k,i) * i! *C_{n}^{i} )

    得到 ( ans = n * sumlimits_{d=0}^{n-1} 2^{C_{n-1}^{2}} * C_{n-1}^{d} * sumlimits_{j=0}^{k} S(k,j) * j! * C_{d}^{j} )

    此时不要把组合数拆成阶乘!虽然拆成阶乘可以消去 ( d! ),但如果不消去,放在一起可以得到新的组合意义;

    ( ans = n * 2^{C_{n-1}^{2}} * sumlimits_{j=0}^{k} S(k,j) * j! * sumlimits_{d=0}^{n-1} C_{n-1}^{d} * C_{d}^{j} )

    而 ( sumlimits_{d=0}^{n-1} C_{n-1}^{d} * C_{d}^{j} ) 表示从 ( n-1 ) 个人里选 ( d ) 个人,再从 ( d ) 个人里选 ( j ) 个人;

    其实就是从 ( n-1 ) 个人里选 ( j ) 个人,剩下的人随便选,即 ( C_{n-1}^{j} * 2^{n-1-j} )

    所以 ( ans = n * 2^{C_{n-1}^{2}} * sumlimits_{j=0}^{k} S(k,j) * j! * C_{n-1}^{j} * 2^{n-1-j} )

    而通过 ( S(n,m) = frac{1}{m!} sumlimits_{k=0}^{m} C_{m}^{k} * (m-k)^{n} * (-1)^{k} ) (枚举 ( k ) 个空组,最后除去 ( m ) 组的排列)

    即 ( S(n,m) = sumlimits_{k=0}^{m} frac{(m-k)^{n}}{(m-k)!} * frac{(-1)^{k}}{k!} )

    可以用NTT求出一行的第二类斯特林数,也就是求出 ( S(k,i) )

    然后把 ( C_{n-1}^{j} ) 拆开约分,上下都只有 ( k ) 级别,预处理即可;

    还是要注意次数是对 ( mod-1 ) 取模。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const xn=2e5+5,xm=(1<<19),mod=998244353;
    int n,m,lim,a[xm],b[xm],rev[xm],jc[xn],jcn[xn],jd[xn];
    int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;}
    ll pw(ll a,ll b)
    {
      ll ret=1; a=a%mod; b=b%(mod-1);
      for(;b;b>>=1,a=(a*a)%mod)if(b&1)ret=(ret*a)%mod;
      return ret;
    }
    void init()
    {
      jc[0]=1;
      for(int i=1;i<=m;i++)jc[i]=(ll)jc[i-1]*i%mod;
      jcn[m]=pw(jc[m],mod-2);
      for(int i=m-1;i>=0;i--)jcn[i]=(ll)jcn[i+1]*(i+1)%mod;
      jd[0]=1;
      for(int j=1;j<=m;j++)jd[j]=(ll)jd[j-1]*(n-j)%mod;
    }
    void ntt(int *a,int tp)
    {
      for(int i=0;i<lim;i++)
        if(i<rev[i])swap(a[i],a[rev[i]]);
      for(int mid=1;mid<lim;mid<<=1)
        {
          int len=(mid<<1),wn=pw(3,tp==1?(mod-1)/len:(mod-1)-(mod-1)/len);
          for(int j=0;j<lim;j+=len)
        for(int k=0,w=1;k<mid;k++,w=(ll)w*wn%mod)
          {
            int x=a[j+k],y=(ll)w*a[j+mid+k]%mod;
            a[j+k]=upt(x+y); a[j+mid+k]=upt(x-y);
          }
        }
      if(tp==1)return; int inv=pw(lim,mod-2);
      for(int i=0;i<lim;i++)a[i]=(ll)a[i]*inv%mod;
    }
    int main()
    {
      scanf("%d%d",&n,&m); init();
      lim=1; int l=0;
      while(lim<=m+m)lim<<=1,l++;
      for(int i=0;i<lim;i++)rev[i]=((rev[i>>1]>>1)|((i&1)<<(l-1)));
      for(int i=0;i<=m;i++)a[i]=(ll)pw(i,m)*jcn[i]%mod;
      for(int i=0;i<=m;i++)b[i]=upt((i&1?-1:1)*jcn[i]);
      ntt(a,1); ntt(b,1);
      for(int i=0;i<lim;i++)a[i]=(ll)a[i]*b[i]%mod;
      ntt(a,-1);
      int ans=0;
      for(int j=0;j<=m;j++)
        ans=(ans+(ll)a[j]*jc[j]%mod*jd[j]%mod*jcn[j]%mod*pw(2,n-1-j))%mod;
      printf("%lld
    ",(ll)n*pw(2,((ll)(n-1)*(n-2)/2))%mod*ans%mod);
      return 0;
    }
  • 相关阅读:
    [kuangbin带你飞]专题十二 基础DP1 E
    hdu 1203 I NEED A OFFER! (01背包)
    hdu 2602 Bone Collector (01背包)
    hdu 4513 吉哥系列故事——完美队形II (manacher)
    hdu 2203 亲和串 (KMP)
    hdu 1686 Oulipo (KMP)
    hdu 1251 统计难题 (字典树)
    hdu 2846 Repository (字典树)
    hdu 1711 Number Sequence (KMP)
    poj 3461 Oulipo(KMP)
  • 原文地址:https://www.cnblogs.com/Zinn/p/10070115.html
Copyright © 2011-2022 走看看