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 ...
随机推荐
- Java基础知识点总结(二)
“https://www.runoob.com/java” 1.Java运算符优先级 2.内置数据类型的包装类 抽象类Number是所有包装类(Integer.Long.Byte.Double.Flo ...
- UML交互图
UML 交互图主要包括对象和消息两类元素,创建交互图的过程实际上就是向对象分配任务的过程,是可视化系统的交互行为. UML 交互图包括两种:序列图和协作图. 序列图:显示对象之间的关系,强调对象之间消 ...
- Vue框架(四)——路由跳转、路由传参、cookies、axios、跨域问题、element-ui模块
路由跳转 三种方式: $router.push / $router.go / router-link to this.$router.push('/course'); this.$router.pus ...
- 【LEETCODE】60、数组分类,适中级别,题目:75、560、105
package y2019.Algorithm.array.medium; /** * @ProjectName: cutter-point * @Package: y2019.Algorithm.a ...
- Python中NumPy的使用一
NumPy简介: 一个用python实现的科学计算,包括:1.一个强大的N维数组对象Array:2.比较成熟的(广播)函数库:3.用于整合C/C++和Fortran代码的工具包:4.实用的线性代数.傅 ...
- C# 简单的定时器使用
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...
- flickity:支持触摸滑动,响应迅速的幻灯片轮播插件
简介:flickity 是一个支持触摸,响应迅速的幻灯片轮播插件.支持环绕滑动.自由滑动.组滑动.自动播放.延迟加载.视差滑动.图片滑动.兼容IE10+, Android 4+, Safari for ...
- modbus协议使用小记
下载了libmodbus库,交叉编译后运行,总是接收回复时不正确.原因不明. 由于使用到modbus的需求比较简单,所以选择直接拼出modbus的请求报文,然后用串口直接发送和接收的方式, 拼modb ...
- python 1.使用djano前的提前准备:虚拟环境 virtualenv
首先想要学习django我们就得了解一下虚拟环境,什么是虚拟环境?他有什么用? 1.首先虚拟环境就相当于一个一个的盒子,这个盒子里面安装的软件不会对其他的盒子造成任何影响. 2.如果你现在用Djang ...
- 一个超实用的python爬虫功能使用 requests BeautifulSoup
一个简单的数据爬取的示例 import os,re import requests import random import time from bs4 import BeautifulSoup us ...