1. 程式人生 > >LOJ #2058「TJOI / HEOI2016」求和

LOJ #2058「TJOI / HEOI2016」求和

不錯的推柿子題

LOJ #2058

題意:求$\sum\limits_{i=0}^n\sum\limits_{j=0}^nS(i,j)·2^j·j!$其中$ S(n,m)$是第二類斯特林數


$ Solution:$

首先考慮第二類斯特林數的意義:將$ n$個有標號元素放入$ m$個無標號集合(無空集)的方案數

我們列舉空集的數量容斥:$ S(n,m)=\frac{1}{m!}\sum\limits_{k=0}^m(-1)^kC_m^k(m-k)^n$

乘上$ \frac{1}{m!}$是因為容斥的集合帶標號而斯特林數本身不帶標號

這樣可以將原式展開得:

$ \sum\limits_{i=0}^n \sum\limits_{j=0}^n2^j \sum\limits_{k=0}^j(-1)^kC_j^k(j-k)^i$     (消階乘項)

把組合數展開得$ \sum\limits_{i=0}^n \sum\limits_{j=0}^n 2^j j! \sum\limits_{k=0}^j \frac{(-1)^k}{k!} \frac{(j-k)^i}{(j-k)!}$

改變列舉順序得$ \sum\limits_{j=0}^n 2^j j! \sum\limits_{k=0}^j \frac{(-1)^k}{k!} \frac{ \sum\limits_{i=0}^n (j-k)^i}{(j-k)!}$

令$ A(x)= \frac{(-1)^x}{x!}$,$ B(x)=\frac{ \sum\limits_{i=0}^n x^i}{x!}$

則原式為$ \sum\limits_{j=0}^n 2^j j! \sum\limits_{k=0}^jA(k)B(j-k)$

容易發現這是一個卷積形式,而函式$ A,B$均可以在$ O(n)$時間複雜度內完成

這樣可以直接用$ NTT$優化,時間複雜度:$ O(n \ log \  n)$


 

$ my \ code:$

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include
<algorithm> #include<queue> #include<vector> #define p 998244353 #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 inv[100010],jc[100010],njc[100010]; int ksm(int x,int y){ int ans=1; for(rt i=y;i;i>>=1,x=1ll*x*x%p)if(i&1)ans=1ll*ans*x%p; return ans; } vector<int>A,B,f,R;int lim=1; int calc(int x,int L,int R){ if(x==1)return R-L+1; return 1ll*(ksm(x,R+1)-ksm(x,L))*ksm(x-1,p-2)%p; } void init(int n){ for(rt i=0;i<=1;i++)inv[i]=jc[i]=njc[i]=1; for(rt i=2;i<=n;i++){ jc[i]=1ll*jc[i-1]*i%p; inv[i]=1ll*inv[p%i]*(p-p/i)%p; njc[i]=1ll*njc[i-1]*inv[i]%p; } while(lim<=n+n)lim<<=1; A.resize(lim);B.resize(lim);f.resize(lim); A[0]=1;for(rt i=1,tag=-1;i<=n;i++,tag*=-1)A[i]=tag*njc[i]; B[0]=1;for(rt i=1;i<=n;i++)B[i]=1ll*njc[i]*calc(i,0,n)%p; } namespace poly{ void getR(int n){ R.resize(n); for(rt i=1;i<n;i++)R[i]=(R[i>>1]>>1)|(i&1)*(n>>1); } void NTT(int n,vector<int>&A,int fla){ for(rt i=0;i<n;i++)if(i>R[i])swap(A[i],A[R[i]]); for(rt i=1;i<n;i<<=1){ int w=ksm(3,(p-1)/2/i); for(rt j=0;j<n;j+=i<<1){ int K=1; for(rt k=0;k<i;k++,K=1ll*K*w%p){ int x=A[j+k],y=1ll*K*A[i+j+k]%p; A[j+k]=(x+y)%p,A[i+j+k]=(x-y)%p; } } } if(fla==-1){ reverse(A.begin()+1,A.end());int invn=ksm(n,p-2); for(rt i=0;i<n;i++)A[i]=1ll*A[i]*invn%p; } } } using namespace poly; int main(){ n=read();init(n); int ans=0;getR(lim); NTT(lim,A,1);NTT(lim,B,1); for(rt i=0;i<lim;i++)f[i]=1ll*A[i]*B[i]%p; NTT(lim,f,-1); for(rt i=0;i<=n;i++)(ans+=1ll*ksm(2,i)*jc[i]%p*f[i]%p)%=p; cout<<(ans+p)%p; return 0; }