题意

设第 $n$ 个Bell数为 $B_n$,求 $B_n \ mod  \ 95041567$.($1 \leq  n  \leq  2^{31}$)

分析

贝尔数的概念和性质,维基百科上有,这里用到两点。

  • 若 $p$ 是任意素数,有 $B_{p+n} = B_n + B_{n+1}(mod \ p)$
  • 每个贝尔数都是相应第二类斯特林数之和,即 $B_n = \sum_{k=1}^nS(n, k)$

贝尔数的这个递推式只适合质数,我们可以将模数拆成质数,然后用CRT合并。

$95041567 = 31 \times 37 \times 41 \times 43 \times 47$,所以预处理前50个,

对于 $n > 50$,使用递推式,递推式可转成矩阵乘法,如下:

$$\begin{bmatrix} 0 & 0 & \cdots  & 1\\
1 & 1 & \cdots & 0\\  \vdots & \vdots &\ddots   & \vdots\\  0 & \cdots & 1 & 1 \end{bmatrix} \times
\begin{bmatrix} B_n\\  B_{n+1}\\  \vdots\\  B_{n+p-1} \end{bmatrix} =
\begin{bmatrix} B_{n+p-1}\\  B_{n+p}\\  \vdots \\  B_{n+2p-2} \end{bmatrix}$$

即 $B_{n+p-1} = A  B_n$

设 $t = n / (p-1), k = n \% (p-1)$,

如果利用 $B_n = A^tB_k$,需要多预处理一倍,但计算时只需求第一个元素;

若利用 $B_{(p-1)t} = A^t B_0$,只需预处理前 $p-1$ 个,但是计算时需要算出第 $k$ 个。

反正两者时间也几乎一样。

时间复杂度为 $O(5\cdot p^3 log{\frac{n}{p}})$

#include<bits/stdc++.h>
using namespace std; typedef long long ll;
const
int maxn =;
const
int mod =; int m[] = {,,,,};
int
Sti[*maxn][*maxn], bell[][*maxn]; //第二类司特林数、贝尔数

void
Stirling2()
{

Sti[][] =;
for
(int i =;i <=*maxn;i++)
for
(int j =;j <= i;j++)
Sti[i][j] = (Sti[i-][j-] + 1LL * j * Sti[i-][j]) % mod;
}
void init()
{

Stirling2();
for
(int i =;i <;i++)
{

bell[i][] =;
for
(int j =;j <=*m[i];j++)
{

bell[i][j] =; //不知道为什么默认不是0
for(int k =;k <= j;k++)
bell[i][j] = (bell[i][j] + Sti[j][k]) % m[i];
//printf("%d\t%d\n",j,bell[i][j]);
}
}
}
struct matrix
{

int
r, c;
int
mat[maxn][maxn];
matrix(){
memset(mat,, sizeof(mat));
}
};
matrix mul(matrix A, matrix B, int p) //矩阵相乘
{
matrix ret;
ret.r = A.r; ret.c = B.c;
for
(int i =;i < A.r;i++)
for
(int k =;k < A.c;k++)
for
(int j =;j < B.c;j++)
{

ret.mat[i][j] = (ret.mat[i][j] + 1LL * A.mat[i][k] * B.mat[k][j]) % p;
}

return
ret;
}
matrix mpow(matrix A, int n, int p)
{

matrix ret;
ret.r = A.r; ret.c = A.c;
for
(int i =;i < ret.r;i++) ret.mat[i][i] =;
while
(n)
{

if
(n &) ret = mul(ret, A, p);
A = mul(A, A, p);
n >>=;
}

return
ret;
}
int solve(int n, int p, int k) //计算Bn % p
{
matrix A;
A.r = A. c = p;
A.mat[][p-] =;
for
(int i =;i < p;i++)
A.mat[i][i-] = A.mat[i][i] =; matrix tmp = mpow(A, n/(p-), p); int ret =;
for
(int i =;i < p;i++)
ret = (ret + tmp.mat[][i] * bell[k][(n%(p-))+i]) % p;
return
ret;} //ax + by = d,且|x|+|y|最小,其中d=gcd(a,b)
//即使a, b在int范围内,x和y也有可能超过int范围
void exgcd(ll a, ll b, ll &d, ll &x, ll &y)
{

if
(!b){ d = a; x =; y =;}
else
{ exgcd(b, a % b, d, y, x); y -= x * (a / b);}
}
//n个方程:x=a[i](mod m[i])
ll china(int n, int* a, int* m)
{

ll M =, d, y, x =;
for
(int i =; i < n; i++) M *= m[i];
for
(int i =; i < n; i++)
{

ll w = M / m[i];
exgcd(m[i], w, d, d, y); //d共用了
x = (x + y * w * a[i]) % M; //x相当于sum
}
return
(x + M) % M;
}
int n;
int
res[]; int main()
{

init(); int T;
scanf("%d", &T);
while
(T--)
{

scanf("%d", &n);
for
(int i =;i <;i++) res[i] = solve(n, m[i], i); // for(int i = 0;i < 5;i++) printf("%d ", res[i]);
// printf("\n");

int
ans = china(, res, m);
printf("%d\n", ans);
}

return
;
}

第二种种写法:

#include<bits/stdc++.h>
using namespace std; typedef long long ll;
const int maxn = ;
const int mod = ; int m[] = {, , , , };
int Sti[maxn][maxn], bell[][maxn]; //第二类司特林数、贝尔数 void Stirling2()
{
Sti[][] = ;
for(int i = ;i <= maxn;i++)
for(int j = ;j <= i;j++)
Sti[i][j] = (Sti[i-][j-] + 1LL * j * Sti[i-][j]) % mod;
} void init()
{
Stirling2();
for(int i = ;i < ;i++)
{
bell[i][] = ;
for(int j = ;j <= m[i];j++)
{
bell[i][j] = ;
for(int k = ;k <= j;k++)
bell[i][j] = (bell[i][j] + Sti[j][k]) % m[i];
//printf("%d\t%d\n",j,bell[i][j]);
}
}
} struct matrix
{
int r, c;
int mat[maxn][maxn];
matrix(){
memset(mat, , sizeof(mat));
}
}; matrix mul(matrix A, matrix B, int p) //矩阵相乘
{
matrix ret;
ret.r = A.r; ret.c = B.c;
for(int i = ;i < A.r;i++)
for(int k = ;k < A.c;k++)
for(int j = ;j < B.c;j++)
{
ret.mat[i][j] = (ret.mat[i][j] + 1LL * A.mat[i][k] * B.mat[k][j]) % p;
}
return ret;
} matrix mpow(matrix A, int n, int p)
{
matrix ret;
ret.r = A.r; ret.c = A.c;
for(int i = ;i < ret.r;i++) ret.mat[i][i] = ;
while(n)
{
if(n & ) ret = mul(ret, A, p);
A = mul(A, A, p);
n >>= ;
}
return ret;
} int solve(int n, int p, int k) //计算Bn % p
{
matrix A;
A.r = A. c = p;
A.mat[][p-] = ;
for(int i = ;i < p;i++)
A.mat[i][i-] = A.mat[i][i] = ; matrix tmp = mpow(A, n/(p-), p); // int ret = 0;
// for(int i = 0;i < p;i++)
// ret = (ret + A.mat[0][i] * bell[k][i]) % p; int ans[p];
for(int i = ;i < p;i++)
{
ans[i] = ;
for(int j = ;j < p;j++)
ans[i] = (ans[i] + 1LL * bell[k][j] * tmp.mat[i][j]) % p;
} return ans[n % (p-)];
} //ax + by = d,且|x|+|y|最小,其中d=gcd(a,b)
//即使a, b在int范围内,x和y也有可能超过int范围
void exgcd(ll a, ll b, ll &d, ll &x, ll &y)
{
if (!b){ d = a; x = ; y = ;}
else{ exgcd(b, a % b, d, y, x); y -= x * (a / b);}
} //n个方程:x=a[i](mod m[i])
ll china(int n, int* a, int* m)
{
ll M = , d, y, x = ;
for (int i = ; i < n; i++) M *= m[i];
for (int i = ; i < n; i++)
{
ll w = M / m[i];
exgcd(m[i], w, d, d, y); //d共用了
x = (x + y * w * a[i]) % M; //x相当于sum
}
return (x + M) % M;
} int n;
int res[]; int main()
{
init(); int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i = ;i < ;i++) res[i] = solve(n, m[i], i); // for(int i = 0;i < 5;i++) printf("%d ", res[i]);
// printf("\n"); int ans = china(, res, m);
printf("%d\n", ans);
}
return ;
}

另外一种方法是利用公式:

$$B_{p^m+n} = mB_n + B_{n+1}$$

于是,我们求 $Bell(n)(mod \ p)$ 时,先把 $n$ 写成 $p$ 进制,即

$$n = a_kp^k +...+a_1p+a_0$$

先预处理 $i < p, \ bell[i] = Bell(i)(mod \ p)$,对于大于 $p$ 的用公式。

#include<bits/stdc++.h>
using namespace std; typedef long long ll;
const int maxn = ;
const int mod = ; int m[] = {, , , , };
int Sti[maxn][maxn], bell[][maxn]; //第二类司特林数、贝尔数 void Stirling2()
{
Sti[][] = ;
for(int i = ;i < maxn;i++)
for(int j = ;j <= i;j++)
Sti[i][j] = (Sti[i-][j-] + 1LL * j * Sti[i-][j]) % mod;
} void init()
{
Stirling2();
for(int i = ;i < ;i++)
{
bell[i][] = ;
for(int j = ;j < maxn;j++)
{
bell[i][j] = ; //不知道为什么默认不是0
for(int k = ;k <= j;k++)
bell[i][j] = (bell[i][j] + Sti[j][k]) % m[i];
//printf("%d\t%d\n",j,bell[i][j]);
}
}
} int solve(int n, int p, int k) //计算Bn % p
{
int a[maxn], B[maxn], d[maxn];
for(int i = ;i <= p;i++) B[i] = bell[k][i]; int cnt = ;
while(n)
{
a[cnt++] = n % p;
n /= p;
} for(int i = ;i < cnt;i++)
for(int j = ;j <= a[i];j++)
{
for(int k = ;k < p;k++)
d[k] = (B[k+] + i*B[k]) % p;
d[p] = (d[] + d[]) % p;
for(int k = ;k <= p;k++) B[k] = d[k];
}
//printf("after\n");
return d[a[]];
} //ax + by = d,且|x|+|y|最小,其中d=gcd(a,b)
//即使a, b在int范围内,x和y也有可能超过int范围
void exgcd(ll a, ll b, ll &d, ll &x, ll &y)
{
if (!b){ d = a; x = ; y = ;}
else{ exgcd(b, a % b, d, y, x); y -= x * (a / b);}
} //n个方程:x=a[i](mod m[i])
ll china(int n, int* a, int* m)
{
ll M = , d, y, x = ;
for (int i = ; i < n; i++) M *= m[i];
for (int i = ; i < n; i++)
{
ll w = M / m[i];
exgcd(m[i], w, d, d, y); //d共用了
x = (x + y * w * a[i]) % M; //x相当于sum
}
return (x + M) % M;
} int n;
int res[]; int main()
{
init(); int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
if(n < maxn)
{
for(int i = ;i < ;i++) res[i] = bell[i][n];
}
else
{
for(int i = ;i < ;i++) res[i] = solve(n,m[i], i);
} // for(int i = 0;i < 5;i++) printf("%d ", res[i]);
// printf("\n"); int ans = china(, res, m);
printf("%d\n", ans);
}
return ;
}

这种方法的复杂度大约为 $O(p^2)$,比前面的方法快了不少。

%%%Acdreamers,链接

参考链接:

1. https://zh.wikipedia.org/w/index.php?title=%E8%B4%9D%E5%B0%94%E6%95%B0

2. https://www.cnblogs.com/yuyixingkong/p/4489189.html

3.https://www.cnblogs.com/Chierush/p/3344661.html

hdu4767 Bell——求第n项贝尔数的更多相关文章

  1. hdu2643&&hdu2512——斯特林数&&贝尔数

    hdu2643 题意:$n$ 个人的排名情况数($n \leq 100$) 分析:考虑 $n$ 个有区别的球放到 $m$ 个有区别的盒子里.无空盒的方案数为 $m!\cdot S(n, m)$. 这题 ...

  2. 用x种方式求第n项斐波那契数,99%的人只会第一种

    大家好啊,我们又见面了.听说有人想学数据结构与算法却不知道从何下手?那你就认真看完本篇文章,或许能从中找到方法与技巧.     本期我们就从斐波那契数列的几种解法入手,感受算法的强大与奥妙吧. 原文链 ...

  3. 贝尔数(来自维基百科)& Stirling数

    贝尔数   贝尔数以埃里克·坦普尔·贝尔(Eric Temple Bell)为名,是组合数学中的一组整数数列,开首是(OEIS的A000110数列):   Bell Number Bn是基数为n的集合 ...

  4. C++求斐波那契数

    题目内容:斐波那契数定义为:f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2)(n>1且n为整数) 如果写出菲氏数列,则应该是: 0 1 1 2 3 5 8 13 21 34 …… ...

  5. HDU 2512 一卡通大冒险(第二类斯特林数+贝尔数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2512 题目大意:因为长期钻研算法, 无暇顾及个人问题,BUAA ACM/ICPC 训练小组的帅哥们大部 ...

  6. bzoj 3501 PA2008 Cliquers Strike Back——贝尔数

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3501 用贝尔三角形 p^2 地预处理 p 以内的贝尔数.可以模(mod-1)(它是每个分解下 ...

  7. HDU 1568 Fibonacci【求斐波那契数的前4位/递推式】

    Fibonacci Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Proble ...

  8. 贝尔数--Codeforces908E. New Year and Entity Enumeration

    给n<=50个长度m<=1000的二进制数,记他们为集合T,求满足下面条件的集合S数:令$M=2^m-1$,1.$a \epsilon S \Rightarrow a \ \ xor \ ...

  9. 线性齐次递推式快速求第n项 学习笔记

    定义 若数列 \(\{a_i\}\) 满足 \(a_n=\sum_{i=1}^kf_i \times a_{n-i}\) ,则该数列为 k 阶齐次线性递推数列 可以利用多项式的知识做到 \(O(k\l ...

随机推荐

  1. pyhthon Opencv截取视频中的图片

    import os import cv2 ##加载OpenCV模块 def video2frames(pathIn='', pathOut='', imgname='', only_output_vi ...

  2. 嵌入式02 STM32 实验07 串口通信

    STM32串口通信(F1系列包含3个USART和2个UART) 一.单片机与PC机串行通信研究目的和意义: 单片机自诞生以来以其性能稳定,价格低廉.功能强大.在智能仪器.工业装备以及日用电子消费产品中 ...

  3. PHP中YUM的理解

    1. YUM是什么? 1)全称:Yellow dog Updater ,Modified. 2)百度简述:是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于RPM包 ...

  4. day12——生成器、推导式、简单内置函数

    day12 生成器 迭代器:python中内置的一种节省空间的工具 生成器的本质就是一个迭代器 迭代器和生成器的区别:一个是pyhton自带的,一个是程序员自己写的 写一个生成器 基于函数 在函数中将 ...

  5. (六)pdf的构成之文件体(pages对象)

    页面树(pages) 通过页面树访问文档的页面,页面树定义PDF文档中的所有页面.树包含表示PDF文档页面的节点,可以是两种类型:中间节点和叶节点.中间节点也称为页面树节点,而叶节点称为页面对象.最简 ...

  6. 【1】volatile关键字解析

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...

  7. server.port 在单元测试中,调用的类或者方法这个地方获取到的端口号就会变成-1

    @Value("${server.port}") 本文链接:https://blog.csdn.net/weixin_38342534/article/details/886985 ...

  8. 矩量母函数(Moment Generating Function,mgf,又称:动差生成函数)

    在统计学中,矩又被称为动差(Moment).矩量母函数(Moment Generating Function,简称mgf)又被称为动差生成函数. 称exp(tξ)的数学期望为随机变量ξ的矩量母函数,记 ...

  9. NIO开发Http服务器(5-完结):HttpServer服务器类

    最近学习了Java NIO技术,觉得不能再去写一些Hello World的学习demo了,而且也不想再像学习IO时那样编写一个控制台(或者带界面)聊天室.我们是做WEB开发的,整天围着tomcat.n ...

  10. Matlab代码优化之道

    一. 遵守Performance Acceleration的规则 关于什么是“Performance Acceleration”请参阅matlab的帮助文件.1.只有使用以下数据类型,matlab才会 ...