题意

设第 $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. 【LEETCODE】60、数组分类,适中级别,题目:75、560、105

    package y2019.Algorithm.array.medium; /** * @ProjectName: cutter-point * @Package: y2019.Algorithm.a ...

  2. JS Web API 拖拽对话框案例

    <style> .login-header { width: 100%; text-align: right; height: 30px; font-size: 24px; line-he ...

  3. MNIST机器学习入门(一)

    一.简介 首先介绍MNIST 数据集.如图1-1 所示, MNIST 数据集主要由一些手写数字的图片和相应的标签组成,图片一共有10 类,分别对应从0-9 ,共10 个阿拉伯数字. 原始的MNIST ...

  4. javaweb之添加学生信息

    1登录账号:要求由6到12位字母.数字.下划线组成,只有字母可以开头:(1分) 2登录密码:要求显示“• ”或“*”表示输入位数,密码要求八位以上字母.数字组成.(1分) 3性别:要求用单选框或下拉框 ...

  5. C#JsonConvert.DeserializeObject反序列化json字符

    需求:需要把第一个id替换掉,在序列化成json dynamic dyn = Newtonsoft.Json.JsonConvert.DeserializeObject(json); foreach ...

  6. 学习docker 部署nginx记录

    docker pull nginx $ docker pull nginx $ docker run --name nginx-test -p 8081:80 -d nginx docker conf ...

  7. (转).Net Core控制台生成exe能独立运行

    原文介绍了两种方式,方式一经测试可用(生成exe在开发机器上可运行),但是因为服务器是windows server2012 r2,没有安装补丁,造成了困难,尚未在服务器上运行成功. (提示 api-m ...

  8. C#关键字:static

    一.static关键字 下面我设计了一个房贷利率上浮类(用来计算房贷利率上浮多少): public class InterestRateGoUp { public InterestRateGoUp() ...

  9. ASP.NET MVC 页面静态化操作的思路

    本文主要讲述了在asp.net mvc中,页面静态化的几种思路和方法.对于网站来说,生成纯html静态页面除了有利于seo外,还可以减轻网站的负载能力和提高网站性能.在asp.net mvc中,视图的 ...

  10. idea下java项目的打包与使用

    一. 打包 (1)打开项目结构,选择Artifacts --> + --> JAR --> From modules with dependencies ... 有main方法就添加 ...