出處0.0
用到第二類斯特林數的性質,做法好像很多,我打的是直接ntt,由第二類斯特林數的容斥公式可以推出,我們可以對于每一個i,來一次ntt求出他與所有j組成的第二類斯特林數的值,這個時候我們是O(n^2logn)的,還不如暴力,但是我們發現,對于剛剛提到的容斥的式子,將其化為卷積形式后,其一邊的每一項對于每一個i都相同,另一邊的每一項是對于所有的i形成一個n項的等比數列,這樣我們可以把成等比數列的一邊求和,用固定的一邊去卷他們的和,這時候的答案的每一項就是所有的i的這一項的和,然后我們再O(n)乘上階乘和2的次冪就可以了.
(一開始代碼打錯了,還以為那個公式在S(i,j)不存在的時候是錯的……后來手玩了一下才發現他是對的……)
補充:
又用多項式求逆打了一遍,比上面那個做法慢了一倍……
這道題求逆的具體做法參見http://blog.csdn.net/lych_cys/article/details/51512278
感覺好神奇啊,把多項式當成數來推式子……
這個東西感覺有點像CDQ+ntt……
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=
400010;
const int P=
998244353;
typedef long long LL;
inline int Pow(
int x,
int y){int ret=
1;while(y){if(y&
1)ret=(LL)ret*x%
P;x=(LL)x*x%P,y>>=
1;}return ret;
}
int A[N],B[N],rev[N],len;
int ai[N],bi[N],ci[N];
int jie[N],ni[N],inv[N],n;
inline void ntt(
int *C,
int opt){register int i,j,k,w;
int wn,temp;for(i=
1;i<len;++i)
if(i<
rev[i])std::swap(C[i],C[rev[i]]);for(k=
2;k<=len;k<<=
1){wn=Pow(
3,(P-
1)/
k);if(opt==-
1)wn=Pow(wn,P-
2);for(i=
0;i<len;i+=
k){w=
1;for(j=
0;j<(k>>
1);++j,w=(LL)w*wn%
P){temp=(LL)w*C[i+j+(k>>
1)]%
P;C[i+j+(k>>
1)]=(C[i+j]-temp+P)%
P;C[i+j]=(C[i+j]+temp)%
P;}}}
}
inline void mul(
int *a,
int *b,
int *c,
int n){len=
1;
while(len<n)len<<=
1;
int i;for(i=
1;i<len;++i)rev[i]=(rev[i>>
1]>>
1)|((i&
1)?(len>>
1):
0);for(i=
0;i<len;++i)A[i]=a[i],B[i]=
b[i];ntt(A,1),ntt(B,
1);for(i=
0;i<len;++i)A[i]=(LL)A[i]*B[i]%
P;ntt(A,-
1);int Inv=Pow(len,P-
2);for(i=
0;i<len;++i)c[i]=(LL)A[i]*Inv%
P;
}
int main(){scanf("%d",&n);
int i,ans=
1,temp=
1;jie[0]=ni[
0]=
1,inv[
1]=
1;for(i=
2;i<=n;++i)inv[i]=((-(LL)(P/i)*inv[P%i])%P+P)%
P;for(i=
1;i<=n;++i)jie[i]=(LL)jie[i-
1]*i%P,ni[i]=(LL)ni[i-
1]*inv[i]%
P;bi[0]=
0,bi[
1]=n,ai[
0]=
1,ai[
1]=P-
1;for(i=
2;i<=n;++
i)bi[i]=(LL)i*(Pow(i,n)-
1+P)%P*ni[i]%P*inv[i-
1]%P,ai[i]=(i&
1)?(P-
ni[i]):ni[i];mul(ai,bi,ci,n+n+
2);for(i=
1;i<=n;++
i)temp=(((LL)temp)<<1LL)%P,ans=(ans+(LL)ci[i]*temp%P*jie[i])%
P;printf("%d\n",ans);return 0;
} 直接ntt #include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=
100010;
const int P=
998244353;
inline int Pow(
int x,
int y){int ret=
1;while(y){if(y&
1)ret=(LL)ret*x%
P;x=(LL)x*x%P,y>>=
1;}return ret;
}
int len,n,A[N<<
2],rev[N<<
2];
int g[N<<
2],f[N<<
2],jie[N],ni[N];
inline void ntt(
int *C,
int opt){register int i,j,k,w;
int wn,temp;for(i=
1;i<len;++i)
if(rev[i]>
i)std::swap(C[i],C[rev[i]]);for(k=
2;k<=len;k<<=
1){wn=Pow(
3,(P-
1)/
k);if(opt==-
1)wn=Pow(wn,P-
2);for(i=
0;i<len;i+=
k){w=
1;for(j=
0;j<(k>>
1);++j,w=(LL)w*wn%
P){temp=(LL)w*C[i+j+(k>>
1)]%
P;C[i+j+(k>>
1)]=(C[i+j]-temp+P)%
P;C[i+j]=(C[i+j]+temp)%
P;}}}
}
inline void Inv(
int *a,
int *b,
int cd){if(cd==
1){b[
0]=Pow(a[
0],P-
2);
return;}Inv(a,b,cd>>
1);int i,inv;len=cd<<
1;for(i=
1;i<len;++i)rev[i]=(rev[i>>
1]>>
1)|((i&
1)?(len>>
1):
0);memcpy(A,a,cd<<
2),memset(A+cd,
0,cd<<
2);ntt(A,1),ntt(b,
1);for(i=
0;i<len;++i)b[i]=(
2-(LL)A[i]*b[i]%P+P)*b[i]%
P;ntt(b,-
1),inv=Pow(len,P-
2);for(i=
0;i<cd;++i)b[i]=(LL)b[i]*inv%
P;memset(b+cd,
0,cd<<
2);
}
int main(){scanf("%d",&n);
int i,cd,ans=
0;jie[0]=ni[
0]=
1;for(i=
1;i<=n;++i)jie[i]=(LL)jie[i-
1]*i%
P;ni[n]=Pow(jie[n],P-
2);for(i=n-
1;i>
0;--i)ni[i]=(LL)ni[i+
1]*(i+
1)%
P;g[0]=
1;for(i=
1;i<=n;++i)g[i]=(-
2*ni[i]+P+P)%
P;cd=
1;while(cd<=n)cd<<=
1;Inv(g,f,cd);for(i=
0;i<=n;++i)ans=(ans+(LL)f[i]*jie[i])%
P;printf("%d\n",ans);return 0;
} 多項式求逆 ?
轉載于:https://www.cnblogs.com/TSHugh/p/8480847.html
總結
以上是生活随笔為你收集整理的【BZOJ 4555】[Tjoi2016Heoi2016]求和 多项式求逆/NTT+第二类斯特林数的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。