【题意】给定n,求Σi=0~nΣj=1~i s(i,j)*2^j*j!,n<=10^5。

【算法】生成函数+排列组合+多项式求逆

【题解】参考: [BZOJ4555][Tjoi2016&Heoi2016]求和-NTT-多项式求逆

$ans=\sum_{i=0}^{n}\sum_{j=0}^{i}s(i,j)*2^j*j!$

令$g(n)=\sum_{j=0}^{n}s(n,j)*2^j*j!$

则ans是Σg(i),只要计算出g(i)的生成函数就可以统计答案。

g(n)可以理解为将n个数划分成若干集合,每个集合有2个属性的排列数。基于此实际意义,通过枚举第一个集合来递推g(n)。

$g(n)=\sum_{i=1}^{n}2*C(n,i)*g(n-i)$

特别的,g(0)=1

两边乘n!(令人窒息的操作),得

$\frac{g(n)}{n!}=\sum_{i=1}^{n}\frac{2}{i!}*\frac{g(n-i)}{(n-i)!}$

这已经是卷积的形式了:

$F(n)=\sum_{i=1}^{n}\frac{2}{n!}*x^i$

$G(n)=\sum_{i=0}^{n}\frac{g(n)}{n!}*x^i$

注意此时F*G卷积后,G(0)的位置是0,所以

$G(n)=F(n)G(n)+1$

移项得,

$G(n)=\frac{1}{1-F(n)}$

最后,多项式求逆即可,复杂度O(n log n)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=,MOD=;
void gcd(int a,int b,int &x,int &y){
if(!b){x=;y=;}else{gcd(b,a%b,y,x);y-=x*(a/b);}
}
int inv(int a){int x,y;gcd(a,MOD,x,y);return (x%MOD+MOD)%MOD;}
int power(int x,int k){
int ans=;
while(k){
if(k&)ans=1ll*ans*x%MOD;
x=1ll*x*x%MOD;
k>>=;
}
return ans;
}
namespace ntt{
int o[maxn],oi[maxn],f[maxn];
void init(int n){
int x=,p=power(,(MOD-)/n);
for(int k=;k<n;k++){
o[k]=x;oi[k]=inv(o[k]);
x=1ll*x*p%MOD;
}
}
void transform(int *a,int n,int *o){
int k=;
while((<<k)<n)k++;
for(int i=;i<n;i++){
int t=;
for(int j=;j<k;j++)if((<<j)&i)t|=(<<(k-j-));
if(i<t)swap(a[i],a[t]);
}
for(int l=;l<=n;l*=){
int m=l>>;
for(int *p=a;p!=a+n;p+=l){
for(int i=;i<m;i++){
int t=1ll*p[i+m]*o[n/l*i]%MOD;
p[i+m]=(p[i]-t+MOD)%MOD;
p[i]=(p[i]+t)%MOD;
}
}
}
}
void dft(int *a,int n){transform(a,n,o);}
void idft(int *a,int n){
transform(a,n,oi);int v=inv(n);
for(int i=;i<n;i++)a[i]=1ll*a[i]*v%MOD;
}
void pinv(int *F,int *g,int n){
if(n==){g[]=inv(F[]);return;}
pinv(F,g,n>>);n<<=;
init(n);//
for(int i=;i<n/;i++)f[i]=F[i],f[i+n/]=;
dft(f,n);dft(g,n);
for(int i=;i<n;i++)g[i]=1ll*g[i]*(-1ll*f[i]*g[i]%MOD+MOD)%MOD;//1ll
idft(g,n);for(int i=n/;i<n;i++)g[i]=;// }
}
int F[maxn],G[maxn],n,fac[maxn];
int main(){
int n,N=;
scanf("%d",&n);n++;
while(N<n)N*=;
fac[]=;
for(int i=;i<n;i++)fac[i]=1ll*fac[i-]*i%MOD;
for(int i=;i<n;i++)F[i]=((-*inv(fac[i]))%MOD+MOD)%MOD;
F[]++;
ntt::pinv(F,G,N);
int ans=;
for(int i=;i<n;i++)ans=(ans+1ll*G[i]*fac[i]%MOD)%MOD;
printf("%d",ans);
return ;
}

注意:多项式求逆过程中每次都要对不同的n进行一次预处理omega[]。

另一种做法

【算法】斯特林数+NTT

【题解】首先有第二类斯特林数的通项公式。

$s(n,m)=\frac{1}{m!}\sum_{k=0}^{m}(-1)^k*C(m,k)*(m-k)^n$

当斯特林数s(n,m)满足m>n时,上述公式计算结果为0,所以第二个Σ可以扩展到n。

$ans=\sum_{i=0}^{n}\sum_{j=0}^{n}s(i,j)*s^j*j!$

代入第二类斯特林数公式。

$ans=\sum_{i=0}^{n}\sum_{j=0}^{n}2^j*j!*\frac{1}{j!}\sum_{k=0}^{j}(-1)^k*\frac{j!}{k!(j-k)!}*(j-k)^i$

通过组合数的分解,向卷积靠拢。

注意到Σi只对最后一个括号有影响,所以移动到最后。

$ans=\sum_{j=0}^{n}2^j*j!\sum_{k=0}^{j}\frac{(-1)^k}{k!}*\frac{\sum_{i=0}^{n}(j-k)^i}{(j-k)!}$

这已经是标准的卷积形式(第二个函数分子是等比数列可以直接计算)。

使用NTT计算。

注意:

1.n=0,只有使0^0=1,斯特林数通项公式才能处理s(0,0)的情况。

2.n=1,等比数列求和公式不能处理Σ1^i即q=1的情况。

所以特殊地,g[0]=1,g[1]=n+1。先计算完再n++。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=,MOD=; int power(int x,int k){
int ans=;
while(k){
if(k&)ans=1ll*ans*x%MOD;
x=1ll*x*x%MOD;
k>>=;
}
return ans;
}
int inv(int x){return power(x,MOD-);}
namespace ntt{
int o[maxn],oi[maxn];
void init(int n){
int x=,g=power(,(MOD-)/n);
for(int k=;k<n;k++){
o[k]=x;oi[k]=inv(o[k]);
x=1ll*x*g%MOD;
}
}
void transform(int *a,int n,int *o){
int k=;
while((<<k)<n)k++;
for(int i=;i<n;i++){
int t=;
for(int j=;j<k;j++)if((<<j)&i)t|=(<<(k-j-));
if(i<t)swap(a[i],a[t]);
}
for(int l=;l<=n;l*=){
int m=l>>;
for(int *p=a;p!=a+n;p+=l){
for(int i=;i<m;i++){
int t=1ll*p[i+m]*o[n/l*i]%MOD;
p[i+m]=(p[i]-t+MOD)%MOD;
p[i]=(p[i]+t)%MOD;
}
}
}
}
void dft(int *a,int n){transform(a,n,o);}
void idft(int *a,int n){
transform(a,n,oi);
int x=inv(n);
for(int i=;i<n;i++)a[i]=1ll*a[i]*x%MOD;
}
}
int n,fac[maxn],f[maxn],g[maxn];
int main(){
scanf("%d",&n);
fac[]=;
for(int i=;i<=n;i++)fac[i]=1ll*fac[i-]*i%MOD;
for(int i=;i<=n;i++)f[i]=1ll*((i&)?MOD-:)*inv(fac[i])%MOD;
for(int i=;i<=n;i++)g[i]=1ll*(-power(i,n+)+MOD)*inv((-i+MOD)%MOD)%MOD*inv(fac[i])%MOD;
g[]=;g[]=n+;//
n++;int N=;//
while(N<n+n)N*=;
ntt::init(N);
ntt::dft(f,N);ntt::dft(g,N);
for(int i=;i<N;i++)f[i]=1ll*f[i]*g[i]%MOD;
ntt::idft(f,N);
int ans=;
for(int i=;i<n;i++)ans=(ans+1ll*power(,i)*fac[i]%MOD*f[i]%MOD)%MOD;
printf("%d",ans);
return ;
}

【BZOJ】4555: [Tjoi2016&Heoi2016]求和 排列组合+多项式求逆 或 斯特林数+NTT的更多相关文章

  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]求和 (多项式求逆)

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=4555 题目大意: 给定 \(S(n,m)\) 表示第二类斯特林数,定义函数 \(f(n ...

  4. BZOJ 4555 [Tjoi2016&Heoi2016]求和 ——分治 NTT 多项式求逆

    不想多说了,看网上的题解吧,我大概说下思路. 首先考察Stirling的意义,然后求出递推式,变成卷积的形式. 然后发现贡献是一定的,我们可以分治+NTT. 也可以直接求逆(我不会啊啊啊啊啊) #in ...

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

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

  6. bzoj 4555: [Tjoi2016&Heoi2016]求和【NTT】

    暴力推式子推诚卷积形式,但是看好多blog说多项式求逆不知道是啥.. \[ \sum_{i=0}^{n}\sum_{j=0}^{n}S(i,j)*2^j*j! \] \[ S(i,j)=\frac{1 ...

  7. [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 ...

  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] 求和 —— 第二类斯特林数+NTT

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4555 关于第二类斯特林数:https://www.cnblogs.com/Wuweizhen ...

随机推荐

  1. lintcode-401-排序矩阵中的从小到大第k个数

    401-排序矩阵中的从小到大第k个数 在一个排序矩阵中找从小到大的第 k 个整数. 排序矩阵的定义为:每一行递增,每一列也递增. 样例 给出 k = 4 和一个排序矩阵: [ [1 ,5 ,7], [ ...

  2. [mysqld_safe]centos7 mysql 安装与配置

    查资料发现是CentOS 7 版本将MySQL数据库软件从默认的程序列表中移除,用mariadb代替了. 有两种解决办法: 安装mariadb [root@a ~]#  yum install mar ...

  3. 运维工程师如果将web服务http专变为https

    1:生成私钥   2:生成证书签署请求   3:在提供CA签署的web网站上,提交生成的证书签署请求   4:下载已经签署的CA证书   5:将证书的信息保留在web服务器中,且应用到提供web服务的 ...

  4. python获取前几天的时间

    days的参数就是你想获取前多少天的数据,如果是昨天的话,则days=1 import datetime today=datetime.date.today() oneday=datetime.tim ...

  5. 【Java】对ArrayList排序

    java如何对ArrayList中对象按照该对象某属性排序 (从小到大) 两种方法: 方法一:Comparator<KNNNode> comparator = new Comparator ...

  6. PHP中与类有关的运算符

    与类有关的运算符: new, instanceof:判断一个“变量”(对象,数据),是否是某个类的“实例”: 示意如下: class  A {} class  B {} class  C extend ...

  7. 第84天:jQuery动态创建表格

    jQuery动态创建表格 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

  8. 【Python】Python流程控制

    1)if条件测试 Python的比较操作 所有的Python对象都支持比较操作 测试操作符('=='操作符测试值的相等性: 'is'表达式测试对象的一致性) Python中不同类型的比较方法 数字:通 ...

  9. SolrPerformanceFactors--官方文档

    原文地址:http://wiki.apache.org/solr/SolrPerformanceFactors Contents Schema Design Considerations indexe ...

  10. [SHOI2011]双倍回文 manacher

    题面: 洛谷:[SHOI2011]双倍回文‘ 题解: 首先有一个性质,本质不同的回文串最多O(n)个. 所以我们可以对于每个i,求出以这个i为结尾的最长回文串,然后以此作为长串,并判断把这个长串从中间 ...