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 ...
随机推荐
- js获取当前时间,格式YYYY-MM-DD
//获取当前时间,格式YYYY-MM-DD function getNowFormatDate() { var date = new Date(); var seperator1 = "-& ...
- c++11多线程记录1 -- std::thread
启动一个线程 话不多说,直接上代码 void func(); int main() { std::thread t(func); //这里就开始启动线程了 t.join(); // 必须调用join或 ...
- 测试类——python编程从入门到实践
1.各种断言方法 常用断言方法: 方法 用途 assertEqual(a, b) 核实a == b assertNotEqual(a, b) 核实a != b assertTrue(x) 核实x为Tr ...
- UI单据按钮点击事件校验
一.按钮点击前事务处理<BeforeEventProcess> public override void BeforeEventProcess(IPart part, string eve ...
- ELK 索引生命周期管理
kibana 索引配置 管理索引 点击设置 --- Elasticsearch 的 Index management 可以查看 elk 生成的所有索引 (设置,Elasticsearch ,管理) 配 ...
- 记一次 WPS Pro 2019 设备和驱动器图标删除
1.图标预览 先看样式 2.软件不能关闭 百度和腾讯网盘都会创建,但是可以软件关闭,WPS以前也可以,现在新版作妖了 3.注册表删除 你做那我就删~Code:HKEY_CURRENT_USER\Sof ...
- OpenResty部署nginx及nginx+lua
因为用nginx+lua去开发,所以会选择用最流行的开源方案,就是用OpenResty nginx+lua打包在一起,而且提供了包括redis客户端,mysql客户端,http客户端在内的大量的组件 ...
- java基础 Math
package cn.mantishell.day08.demo04; /** * java.util.Math类是数学相关的工具类,里面提供类大量的静态方法,完成与数学运算相关的操作 * * pub ...
- 自学Python编程的第九天(希望有大牛帮我看看我第一个代码是否有弊端,感谢您们)----------来自苦逼的转行人
2019-09-19-22:11:33 今天是自学Python的第九天 学的内容是有关文件操作的,如:r.w.a.rb.wb.ab.r+.w+.a+等 有大牛帮我看一下我的代码第一个有没有什么弊端吗? ...
- 记录screen屏幕日志
1.建立日志存放目录#mkdir /var/log/screen/ 2.修改配置文件,在末尾添加配置内容#vi /etc/screenrclogfile /var/log/screen/%t.log ...