[第二类斯特林数 组合计数] 省选模拟赛 2 B. 两弹一星 missile

题目大意

一张无向图的权值定义为 xk ,其中 x 是图中结构为树的连通块个数。给定 n, k , 求出所有 n 个点带标号的的简单无向图的权值和,对 998244353 取模。

xi 表示树联通块 i 是否存在 , 图的权值为 (xi)m 。对于某 k 个联通块,如果同时出现,那么贡献为 S(m,k)k!
fn 为 n个点的树连通图个数,直接由prufer得出
gi,j 为 i 个点 j 个树连通块的个数,通过简单 dp 求得
再将求出来的东西套入之前的斯特林数式子就可以计算答案
dp用FFT 来优化

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

const int N=200005;
const int P=998244353;
const int G=3;

inline ll Pow(ll a,int b){
  ll ret=1;
  for (;b;b>>=1,a=a*a%P)
    if (b&1)
      ret=ret*a%P;
  return ret;
}

int num;
int w[2][N];
int R[N];

inline void Init(int n){
  int x=n,L=0; num=n;
  while (n>>=1) L++;
  for (int i=1;i<num;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
  ll g=Pow(G,(P-1)/num);
  w[1][0]=w[0][0]=1;
  for (int i=1;i<num;i++) w[1][i]=(ll)w[1][i-1]*g%P;
  for (int i=1;i<num;i++) w[0][i]=w[1][num-i];
}


inline void FFT(int *a,int n,int r){
  for (int i=0;i<n;i++) if (i<R[i]) swap(a[i],a[R[i]]);
  for (int i=1;i<n;i<<=1)
    for (int j=0;j<n;j+=(i<<1))
      for (int k=0;k<i;k++){
    ll x=a[j+k],y=(ll)w[r][num/(i<<1)*k]*a[j+i+k]%P;
    a[j+k]=(x+y)%P; a[j+i+k]=(x+P-y)%P;
      }
  if (!r) for (int i=0,inv=Pow(n,P-2);i<n;i++) a[i]=(ll)a[i]*inv%P;
}

ll fac[N],inv[N],S[25][25];

inline ll C(int n,int m){
  if (n<m) return 0;
  return fac[n]*inv[m]%P*inv[n-m]%P;
}

ll f[N],g[25][N];

inline void Pre(int n,int K){
  fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%P;
  inv[1]=1; for (int i=2;i<=n;i++) inv[i]=(ll)(P-P/i)*inv[P%i]%P;
  inv[0]=1; for (int i=1;i<=n;i++) inv[i]=inv[i]*inv[i-1]%P;
  f[1]=1; for (int i=2;i<=n;i++) f[i]=Pow(i,i-2);
  S[0][0]=1;
  for (int i=1;i<=K;i++){
    S[i][0]=0;
    for (int j=1;j<=i;j++)
      S[i][j]=(S[i-1][j-1]+(ll)S[i-1][j]*j%P)%P;
  }
}

int n,K; ll Ans;

int m;
int A[N],B[N];

inline void Calc_G(ll *ng,ll *g){
  for (int i=0;i<m;i++) A[i]=B[i]=0;
  for (int i=0;i<=n;i++)
    A[i]=(ll)g[i]*inv[i]%P;
  for (int i=1;i<=n;i++)
    B[i]=(ll)f[i]*inv[i-1]%P;
  FFT(A,m,1); FFT(B,m,1);
  for (int i=0;i<m;i++)
    A[i]=(ll)A[i]*B[i]%P;
  FFT(A,m,0);
  for (int i=1;i<=n;i++)
    ng[i]=(ll)A[i]*fac[i-1]%P;
}


int main(){
  freopen("missile.in","r",stdin);
  freopen("missile.out","w",stdout);
  scanf("%d%d",&n,&K); Pre(n,K);
  for (m=1;m<=(n<<1);m<<=1);
  Init(m);
  g[0][0]=1;
  for (int j=1;j<=K;j++)
    Calc_G(g[j],g[j-1]);
  Ans=0;
  for (int i=1;i<=n;i++)
    for (int j=1;j<=K;j++)
      (Ans+=g[j][i]*C(n,i)%P*Pow(2,C(n-i,2))%P*S[K][j]%P*fac[j]%P)%=P;
  printf("%d\n",Ans);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值