您的位置:首页 > 其它

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

2017-03-18 21:01 429 查看
题目大意

一张无向图的权值定义为 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]
;
int R
;

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
,inv
,S[25][25];

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

ll f
,g[25]
;

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
,B
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: