hdu4767 Bell——求第n项贝尔数
题意
设第 $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项贝尔数的更多相关文章
- hdu2643&&hdu2512——斯特林数&&贝尔数
hdu2643 题意:$n$ 个人的排名情况数($n \leq 100$) 分析:考虑 $n$ 个有区别的球放到 $m$ 个有区别的盒子里.无空盒的方案数为 $m!\cdot S(n, m)$. 这题 ...
- 用x种方式求第n项斐波那契数,99%的人只会第一种
大家好啊,我们又见面了.听说有人想学数据结构与算法却不知道从何下手?那你就认真看完本篇文章,或许能从中找到方法与技巧. 本期我们就从斐波那契数列的几种解法入手,感受算法的强大与奥妙吧. 原文链 ...
- 贝尔数(来自维基百科)& Stirling数
贝尔数 贝尔数以埃里克·坦普尔·贝尔(Eric Temple Bell)为名,是组合数学中的一组整数数列,开首是(OEIS的A000110数列): Bell Number Bn是基数为n的集合 ...
- 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 …… ...
- HDU 2512 一卡通大冒险(第二类斯特林数+贝尔数)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2512 题目大意:因为长期钻研算法, 无暇顾及个人问题,BUAA ACM/ICPC 训练小组的帅哥们大部 ...
- bzoj 3501 PA2008 Cliquers Strike Back——贝尔数
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3501 用贝尔三角形 p^2 地预处理 p 以内的贝尔数.可以模(mod-1)(它是每个分解下 ...
- HDU 1568 Fibonacci【求斐波那契数的前4位/递推式】
Fibonacci Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Proble ...
- 贝尔数--Codeforces908E. New Year and Entity Enumeration
给n<=50个长度m<=1000的二进制数,记他们为集合T,求满足下面条件的集合S数:令$M=2^m-1$,1.$a \epsilon S \Rightarrow a \ \ xor \ ...
- 线性齐次递推式快速求第n项 学习笔记
定义 若数列 \(\{a_i\}\) 满足 \(a_n=\sum_{i=1}^kf_i \times a_{n-i}\) ,则该数列为 k 阶齐次线性递推数列 可以利用多项式的知识做到 \(O(k\l ...
随机推荐
- Fineui alert去掉关闭按钮,去掉X
参考:http://fineui.com/bbs/forum.php?mod=viewthread&tid=7455&highlight=alert http:// ...
- [转帖]redhat7.6Linux安装Oracle19C完整版教程
redhat7.6Linux安装Oracle19C完整版教程 https://www.oracle.com/technetwork/database/enterprise-edition/downlo ...
- 深层目录文件复制,C# 递归,录音录像图片文件过多,用于测试程序
/// <summary> /// 录音录像图片文件过多只复制目录的前几个文件,用于测试程序 /// d:\file/images/2019-10/01/01/xxxxx.jpg(前几个文 ...
- js确定取消—js确定取消判断
国瑞前端: js确定取消,在html界面中,有css模拟的模态框,这样显示的就会更好看一些,那么javascript有没有自带的弹框呢,当然是有的,接下来我就来给大家介绍一下把: js确定取消-警告框 ...
- pytest_assert断言
前言 断言是写自动化测试基本最重要的一步,一个用例没有断言,就失去了自动化测试的意义了.什么是断言呢? 简单来讲就是实际结果和期望结果去对比,符合预期那就测试pass,不符合预期那就测试 failed ...
- MySQl数据库面试题
1. MySQL中索引什么作用? 索引的定义和创建的目的 1) 索引是对数据库表中一列或者多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息 2) 索引的分类:主键索引,唯一索引,常规 ...
- python 函数的链式调用(一个函数调用使用两个括号)
# python 函数的链式调用 def funcA(a): def funcB(b): for a_each in a: x = funcB(a_each) return x return func ...
- Java调用Http/Https接口(4)--HttpClient调用Http/Https接口
HttpClient是Apache HttpComponents项目下的一个组件,是Commons-HttpClient的升级版,两者api调用写法也很类似.文中所使用到的软件版本:Java 1.8. ...
- Java之路---Day04
2019-10-18-21:35:36 面向对象 学面向对象前要先知道什么叫面向过程,了解了什么叫面向过程才容易理解面向对象 面向过程(强调步骤) 概念:当需要实现一个功能的时候,每一个具体的步骤都要 ...
- localStorage的增删改查
var _localStorage = window.localStorage; undefined /* 增 */ _localStorage.name = '张泰松' "张泰松" ...