[HEOI2016]求和 sum

标签: NTT cdq分治 多项式求逆 第二类斯特林数


Description

求$$\sum_{i=0}n\sum_{j=0}i S(i,j)×2^j×(j!)$$

其中S(i,j)代表第二类斯特林数。

Solution

解法一

记Bell数\(B(n)=\sum_{i=0}^nS(n,i)\)

根据第二类斯特林数的组合意义,\(B(i)\)代表把n个球放进任意个相同的盒子的方案数。

那么有$$B(n)=\sum_{i=0}^{n-1} C(n-1,i) B(i)$$

讨论一下有多少个球与第一个球同盒即可。

记\(B'(n)=\sum_{i=0}^nS(n,i)×i!\)

这个的组合意义是把n个求放进任意个不同盒子的方案数。

有$$B'(n)=\sum_{i=0}^{n-1} C(n,i)B'(i)$$

讨论一下第一个盒子有多少个球

现在记\(f(n)=\sum_{i=0}^nS(n,i)×2^i×i!\)

这个的组合意义是把n个球放进任意个不同且有两种颜色盒子的方案数。

显然$$f(n)=2\sum_{i=0}^{n-1} C(n,i)f(i)$$

相当于每个盒子有两种不同的颜色选。

化简式子

\[{f(n)\over n!}=\sum_{i=0}^{n-1}{f(i)\over i!} {2 \over (n-i)!}
\]

设\(g(i)={2 \over i!}x^i\),特别地,\(g(0)=0\).

这就是一个卷积的形式了。

然后cdq+NTT即可。

解法2

以上面的式子为基础做多项式求逆。

构造一个生成函数\(F(x)=\sum_{i=0}^{\infty}{f(i) \over i!}x^i\)与\(G(x)=\sum _{i=1}^{\infty}g(i)\)

那么根据上面的递推式子,有\(F(x)=F(x)×G(x)+1\)

解得\(F(x)={1 \over {1-G(x)}}\)

解法3

考虑斯特林数的容斥求法。

我们令\(f(i)=\sum_{j=i}^nS(j,i)\)

那么答案显然是\(\sum_{i=0}^nf(i)2^ii!\)

接下来考虑怎么求\(f(i)\)

\[f(i)=\sum_ {j=i}^nS(j,i)=\sum_ {j=0}^nS(j,i)
\\ =\sum_ {j=0}^n {\frac 1 {i!}}\sum_{k=0}^i (-1)^kC(i,k)(i-k)^j
\\ =\sum_ {k=0} {(-1)^k \over k!} {\sum_{j=0}^n (i-k)^j \over (i-k)! }
\]

一遍NTT即可。

Code

解法一

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
int sum=0,p=1;char ch=getchar();
while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
if(ch=='-')p=-1,ch=getchar();
while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum*p;
} const int maxn=4e5+20;
const int mod=998244353; inline int power(int a,int b)
{
int ans=1;
while(b)
{
if(b & 1)ans=(ll)ans*a%mod;
b>>=1;
a=(ll)a*a%mod;
}
return ans;
} int n,f[maxn],g[maxn],jc[maxn],jcn[maxn],inv[maxn],rev[maxn]; void init()
{
n=read();
jc[0]=jcn[0]=jc[1]=jcn[1]=inv[1]=1;
REP(i,2,n)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod,jc[i]=(ll)i*jc[i-1]%mod,jcn[i]=(ll)inv[i]*jcn[i-1]%mod;
REP(i,1,n)g[i]=jcn[i]*2%mod;
} int A[maxn],B[maxn]; inline void NTT(int *p,int N,int op)
{
int n=1,l=0;while(n<N)n<<=1,l++;
REP(i,1,n-1)rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
REP(i,0,n-1)if(i<rev[i])swap(p[i],p[rev[i]]);
for(int i=1;i<n;i<<=1)
{
int W=power(3,(mod-1)/(i<<1));
for(int j=0;j<n;j+=i<<1)
{
int w=1;
for(int k=j;k<i+j;k++,w=(ll)w*W%mod)
{
int x=p[k],y=(ll)p[k+i]*w%mod;
p[k]=x+y;p[k+i]=x-y;
if(p[k]>=mod)p[k]-=mod;
if(p[k+i]<0)p[k+i]+=mod;
}
}
}
if(op==-1)
{
int inv=power(n,mod-2);
REP(i,0,n-1)p[i]=(ll)p[i]*inv%mod;
reverse(p+1,p+n);
}
} void solve(int l,int r)
{
if(l==r)return;
int mid=(l+r)>>1;
solve(l,mid);
REP(i,0,mid-l)A[i]=f[i+l];
REP(i,0,r-l)B[i]=g[i];
int N=1;
while(N<=(mid-l)+(r-l)+1)N<<=1;
NTT(A,N,1);NTT(B,N,1);
REP(i,0,N-1)A[i]=(ll)A[i]*B[i]%mod;
NTT(A,N,-1);
REP(i,mid-l+1,r-l)f[i+l]=(A[i]+f[i+l])%mod;
REP(i,0,N-1)A[i]=B[i]=0;
solve(mid+1,r);
} void doing()
{
f[0]=1;
solve(0,n);
int ans=0;
REP(i,0,n)ans=(ans+(ll)f[i]*jc[i])%mod;
printf("%d\n",ans);
} int main()
{
init();
doing();
return 0;
}

解法二

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
int sum=0,p=1;char ch=getchar();
while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
if(ch=='-')p=-1,ch=getchar();
while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum*p;
} const int maxn=3e5+20;
const int mod=998244353; int n,jc[maxn],jcn[maxn],inv[maxn],rev[maxn]; inline int power(int a,int b)
{
int ans=1;
while(b)
{
if(b & 1)ans=(ll)ans*a%mod;
b>>=1;
a=(ll)a*a%mod;
}
return ans;
} inline void init()
{
n=read();jc[0]=jcn[0]=jc[1]=jcn[1]=inv[1]=1;
REP(i,2,n)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod,jc[i]=(ll)i*jc[i-1]%mod,jcn[i]=(ll)inv[i]*jcn[i-1]%mod;
} int g[maxn],ans[maxn],A[maxn],B[maxn],C[maxn]; inline void NTT(int *p,int N,int op)
{
int n=1,l=0;while(n<N)n<<=1,l++;
REP(i,0,n-1)rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
REP(i,0,n-1)if(i<rev[i])swap(p[i],p[rev[i]]);
for(int i=1;i<n;i<<=1)
{
int W=power(3,(mod-1)/(i<<1));
for(int j=0;j<n;j+=i<<1)
{
int w=1;
for(int k=j;k<i+j;k++,w=(ll)W*w%mod)
{
int x=p[k],y=(ll)w*p[k+i]%mod;
p[k]=x+y;p[k+i]=x-y;
if(p[k]>=mod)p[k]-=mod;
if(p[k+i]<0)p[k+i]+=mod;
}
}
}
if(op==-1)
{
int inv=power(n,mod-2);
REP(i,0,n-1)p[i]=(ll)p[i]*inv%mod;
reverse(p+1,p+n);
}
} void Inv(int *p,int *q,int len)
{
if(len==1){ q[0]=power(p[0],mod-2);return;}
Inv(p,q,len>>1);
REP(i,0,len-1)A[i]=p[i],B[i]=q[i];
NTT(A,len<<1,1);NTT(B,len<<1,1);
REP(i,0,(len<<1)-1)A[i]=(ll)A[i]*B[i]%mod*B[i]%mod;
NTT(A,len<<1,-1);
REP(i,0,len-1)q[i]=((2*q[i]-A[i])%mod+mod)%mod;//len不需要左移
REP(i,0,(len<<1)-1)A[i]=B[i]=0;
} inline void doing()
{
int N=1,l=0;
REP(i,1,n)g[i]=(-jcn[i]*2+2*mod)%mod;
g[0]=1;
while(N<=n)N<<=1,l++;
Inv(g,ans,N);
int Ans=0;
REP(i,0,n)Ans=(Ans+(ll)ans[i]*jc[i])%mod;
printf("%d\n",Ans);
} int main()
{
init();
doing();
return 0;
}

解法三

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
int sum=0,p=1;char ch=getchar();
while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
if(ch=='-')p=-1,ch=getchar();
while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum*p;
} const int maxn=4e5+20;
const int mod=998244353; int n,inv[maxn],jc[maxn],jcn[maxn]; inline int power(int a,int b)
{
int ans=1;
while(b)
{
if(b & 1)ans=(ll)ans*a%mod;
b>>=1;
a=(ll)a*a%mod;
}
return ans;
} int f[maxn],g[maxn],rev[maxn]; inline void NTT(int *p,int n,int opt)
{
REP(i,0,n-1)if(i<rev[i])swap(p[i],p[rev[i]]);
for(int i=1;i<n;i<<=1)
{
int W=power(3,(mod-1)/(i<<1));
for(int j=0;j<n;j+=i<<1)
{
int w=1;
for(int k=j;k<i+j;k++,w=(ll)w*W%mod)
{
int x=p[k],y=(ll)p[k+i]*w%mod;
p[k]=x+y;
p[k+i]=x-y;
if(p[k]>=mod)p[k]-=mod;
if(p[k+i]<0)p[k+i]+=mod;
}
}
}
if(opt==-1)
{
int inv=power(n,mod-2);
REP(i,0,n-1)p[i]=(ll)p[i]*inv%mod;
reverse(p+1,p+n);
}
} void init()
{
n=read();inv[1]=jc[0]=jc[1]=jcn[0]=jcn[1]=1;
REP(i,2,n+1)jc[i]=(ll)i*jc[i-1]%mod,inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod,jcn[i]=(ll)inv[i]*jcn[i-1]%mod;
f[0]=g[0]=1;g[1]=n+1;
REP(i,1,n)f[i]=(ll)(mod-inv[i])*f[i-1]%mod;
REP(i,2,n)g[i]=(ll)(power(i,n+1)-1)*inv[i-1]%mod*jcn[i]%mod;
int N=1,l=0;while(N<=2*n)N<<=1,l++;
REP(i,1,N-1)rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
NTT(f,N,1);
NTT(g,N,1);
REP(i,0,N-1)f[i]=(ll)f[i]*g[i]%mod;
NTT(f,N,-1);
} void doing()
{
int ans=0,bin=1;
REP(i,0,n)ans=(ans+(ll)f[i]*jc[i]%mod*bin)%mod,bin=(bin<<1)%mod;
printf("%d\n",ans);
} int main()
{
init();
doing();
return 0;
}

[HEOI2016]求和 sum的更多相关文章

  1. BZOJ 4555: [Tjoi2016&Heoi2016]求和 [分治FFT 组合计数 | 多项式求逆]

    4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...

  2. BZOJ 4555: [Tjoi2016&Heoi2016]求和 [FFT 组合计数 容斥原理]

    4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...

  3. 【BZOJ】4555: [Tjoi2016&Heoi2016]求和 排列组合+多项式求逆 或 斯特林数+NTT

    [题意]给定n,求Σi=0~nΣj=1~i s(i,j)*2^j*j!,n<=10^5. [算法]生成函数+排列组合+多项式求逆 [题解]参考: [BZOJ4555][Tjoi2016& ...

  4. 【BZOJ 4555】 4555: [Tjoi2016&Heoi2016]求和 (NTT)

    4555: [Tjoi2016&Heoi2016]求和 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 315  Solved: 252 Des ...

  5. [BZOJ4555][TJOI2016&HEOI2016]求和(分治FFT)

    4555: [Tjoi2016&Heoi2016]求和 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 525  Solved: 418[Sub ...

  6. bzoj 4555 [Tjoi2016&Heoi2016]求和 NTT 第二类斯特林数 等比数列求和优化

    [Tjoi2016&Heoi2016]求和 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 679  Solved: 534[Submit][S ...

  7. [BZOJ4555 TJOI2016 HEOI2016 求和]

    ​ 第一篇博客,请大家多多关照.(鞠躬 BZOJ4555 TJOI2016 HEOI2016 求和 题意: ​ 给定一个正整数\(n\)(\(1\leqq n \leqq100000\)),求: \[ ...

  8. BZOJ 4555: [Tjoi2016&Heoi2016]求和 (NTT + 第二类斯特林数)

    题意 给你一个数 \(n\) 求这样一个函数的值 : \[\displaystyle f(n)=\sum_{i=0}^{n}\sum_{j=0}^{i} \begin{Bmatrix} i \\ j ...

  9. [BZOJ 4555][Tjoi2016&Heoi2016]求和

    题意 给定 $n$ , 求下式的值: $$ f(n)= \sum_{i=0}^n\sum_{j=0}^i\begin{Bmatrix}i\\ j\end{Bmatrix}\times 2^j\time ...

随机推荐

  1. 【编程技巧】EXTJS中Ext.grid.GridPanel配置项autoExpandColumn的使用方法

    autoExpandColumn的作用是自动伸展,占满剩余区域.一般使用在列比较少,并且大多数列都比较窄,有一列比较宽的情况下,当然什么时候使用,还是得按照实际情况确定. 使用的时候主要有三点要注意的 ...

  2. virtuoso装载大的rdf文件的方法

    本文详细介绍了将一个比较大的rdf文件装载到virtuoso数据库的过程.参考virtuoso网站的文档说明,通过实践,将一个大约4.6G左右的nt文件装载到virtuoso数据库中,用了大概6个多小 ...

  3. python基础8之自定义模块、if __name__==__main__:解释

    一.自定义模块与使用 python模块说明:类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个函数才 ...

  4. Centos硬盘IO性能检测命令iostat[转]

    Centos硬盘IO性能检测命令iostat[转] 在Linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为caching.这个问题,貌似有不少人在问,不过都 ...

  5. error: stray '\357' in program编程出错的总结

    错误: 编译报错:error: stray '\357' in program 原因:在程序中打入了全角字符   具体分析产生原因: 在编程中,由于打字的快速,按下ctrl键后紧接着按下了space键 ...

  6. CSS3 三角形运用

      酷酷的 CSS3 三角形运用 概述 在早期的前端Web设计开发年代,完成一些页面元素时,我们必须要有专业的PS美工爸爸,由PS美工爸爸来切图,做一些圆角.阴影.锯齿或者一些小图标. 在CSS3出现 ...

  7. c++ singleton单例模式

    方法1:加锁的经典懒汉实现: class singleton { public: static pthread_mutex_t mutex; static singleton* initance(); ...

  8. Matlab产生TestBeach所需要的波形数据

    在用vivado仿真的时候,很多情况下需要自己产生波形来代替AD采样波形.以前的做法都是用DDS内部产生所需要的波形来模仿外部输入,后来发现这种做法不仅麻烦,而且不易修改,对仿真很不友好.于是改用ma ...

  9. 【转】SED多行模式空间

    在前面看到的都是单行模式.每次sed处理一个行. 但是sed是允许一次处理多行的.这就是所谓的多行模式空间. 多行模式空间命令有(N.D.P),他们分别对应单行模式空间(n.d.p). 分别是他们的多 ...

  10. 深入浅出Hadoop之HDFS

    hadoop生态系统一直是大数据灵域的热点,其中包括今天要聊的HDFS,和计划以后想聊的yarn, mapreduce, spark, hive, hbase, 已经聊过的zookeeper,等等. ...