【题解】P5364 [SNOI2017]礼物
提供一个讨论区有人提出但没细讲的斯特林数做法,复杂度 \(O(k^2)\) 且可优化到 \(O(k \log k)\)。
前置知识:第二类斯特林数的常用性质。
下文中为了方便设 \(m=n-1\)。
首先发现题目让我们求 \(\sum_{i=1}^m 2^{m-i} \times i^k +n^k\),这个式子的推导别的题解都有写我就不写了。
为方便,考虑求 \(\sum_{i=0}^m 2^{m-i} \times i^k\)。
可以发现它和常见的第二类斯特林数应用中的求自然数 \(k\) 次幂和很像,所以考虑用同样的方法做。
代入第二类斯特林数的性质式子 \(i^k=\sum_{j=0}^k{k \brace j}j!{i \choose j}\),推一波式子:
\[\begin{aligned}
ans
&=\sum_{i=0}^m 2^{m-i} \times i^k \\
&=\sum_{i=0}^m 2^{m-i} \sum_{j=0}^k{k \brace j}j!{i \choose j} \\
&=\sum_{j=0}^k {k \brace j}j! \sum_{i=0}^m 2^{m-i} {i \choose j} \\
&=\sum_{j=0}^k {k \brace j}j! \sum_{i=j}^m 2^{m-i} {i \choose j} \\
\end{aligned}
\]
设 \(g(p)=\sum_{i=p}^m 2^{m-i} {i \choose p}\),这个式子怎么求呢?考虑递推搞出下一项:
\[\begin{aligned}
g(p)
&=\sum_{i=p}^m 2^{m-i} {i \choose p} \\
&=\sum_{i=p+1}^m 2^{m-i} {i \choose p}+2^{m-p}\\
&=\sum_{i=p+1}^m 2^{m-i} \times ({i+1 \choose p+1}-{i \choose p+1})+2^{m-p}\\
&=\sum_{i=p+1}^m 2^{m-i} \times {i+1 \choose p+1}-\sum_{i=p+1}^m 2^{m-i}{i \choose p+1}+2^{m-p}\\
&=\sum_{i=p+2}^{m+1} 2^{m-i+1} {i \choose p+1}-g(p+1) +2^{m-p}\\
&={m+1 \choose p+1}-g(p+1) +2\sum_{i=p+2}^{m} 2^{m-i} {i \choose p+1} +2 \times 2^{m-(p+1)}\\
&={m+1 \choose p+1}-g(p+1) +2\sum_{i=p+1}^{m} 2^{m-i} {i \choose p+1}\\
&={m+1 \choose p+1} +g(p+1)
\end{aligned}
\]
反过来,有 \(g(p)=g(p-1)-{m+1 \choose p} =g(p-1)-{n \choose p}\)。
于是求出边界 \(g(0)=\sum_{i=0}^m 2^{m-i} {i \choose 0}=\sum_{i=0}^m 2^i=2^{m+1}-1=2^n-1\) 然后递推即可求出 \(g\)。
所以只要枚举 \(j\),算出 \(\sum_{j=0}^k {k \brace j}j!g(j)\) 即可算出答案。
复杂度 \(O(k^2)\),瓶颈在于预处理第二类斯特林数,用 NTT 卷积求一行的第二类斯特林数可优化到 \(O(k \log k)\)。
主要部分代码(马蜂较诡异,勿喷):
inline ll md(ll x){return x<0?x+mod:x;}
inline ll qpow(ll d,ll z){
ll res=1ll;
for(;z;z>>=1ll,d=d*d%mod) if(z&1ll) res=res*d%mod;
return res;
}
signed main(){
ll n=read();int k=read();
inv[0]=inv[1]=stl[1][1]=1ll;
for(re int i=2;i<=k;++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(re int i=2;i<=k;++i) for(re int j=1;j<=i;++j) stl[i][j]=(stl[i-1][j-1]+j*stl[i-1][j])%mod;
ll f=1ll,g=md(qpow(2ll,n)-1),c=1ll,ans=qpow(n%=mod,k);
for(re int j=0;j<=k;++j,f=f*j%mod,g=md(g-(c=c*(n-j+1ll)%mod*inv[j]%mod))) ans=(ans+stl[k][j]*f%mod*g)%mod;
printf("%lld\n",ans);
return 0;
}