【BZOJ3501】[PA2008] Cliquers Strike Back(贝尔数)
- 设\(x\)为\(n\)个有标号元素的集合划分数目,求\(m^x(mod\ 99999599)\)。
- \(n,m\le10^{18}\)
贝尔数及其性质
关于贝尔数可见这篇博客:浅谈贝尔数及其性质。
然后我们发现要求的\(x\)就是\(B_n\)。
题目中已经很良心地给出了\(999999598=2\times13\times5281\times7283\),因此我们考虑分别求出在这四个质数模数意义下的答案,然后中国剩余定理合并。
模数变成了质数,就可以利用贝尔数\(Touchard\)同余的性质:
\[B_{n+p}\equiv B_n+B_{n+1}(mod\ p)\\
B_{n+p^m}\equiv mB_n+B_{n+1}(mod\ p)
\]
因此,只要给\(n\)做一个\(p\)进制拆分,然后从低位往高位枚举,对于第\(i\)位要依次加上一个\(n'_i\)个\(p^i\)。
我们只要维护好当前的\(B_{n\sim n+p-1}\)即可。特殊地,利用\(B_{n+p}\equiv B_n+B_{n+1}\)的性质也可以直接求出\(B_{n+p}\),用于求出新的\(B_{n+p-1}\)。
一个奇怪的常数问题,一开始我用贝尔数的递推式预处理结果\(TLE\)了,然后换用贝尔数是第二类斯特林数的和的性质预处理就过了?!
代码:\(O(\sum p^2logn)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define V 7283
#define X 999999599
#define LL long long
using namespace std;
const int Pt=4,p[Pt]={2,13,5281,7283};
LL n,m;I int QP(RI x,RI y,CI p) {RI t=1;W(y) y&1&&(t=1LL*t*x%p),x=1LL*x*x%p,y>>=1;return t;}
int B[V+5],S[2][V+5];I void Init()//用第二类斯特林数和来预处理贝尔数
{
RI i,j,op;for(op=S[0][0]=B[0]=i=1;i<=V;op^=1,++i) for(S[op][0]=0,
j=1;j<=i;++j) S[op][j]=(1LL*j*S[op^1][j]+S[op^1][j-1])%(X-1),B[i]=(B[i]+S[op][j])%(X-1);
}
int v[60],b[V+5];I int Calc(CI P)//求解模数为P的答案
{
RI i,j,k,t=-1;LL x=n;W(x) v[++t]=x%P,x/=P;for(k=0;k<=P;++k) b[k]=B[k]%P;//P进制拆分
for(i=1;i<=t;++i) for(j=1;j<=v[i];++j) {for(k=0;k^P;++k) b[k]=(1LL*i*b[k]+b[k+1])%P;b[P]=(b[0]+b[1])%P;}//从低位往高位枚举,加上v[i]个p^i
return b[v[0]];//根据第0位的值返回答案
}
int main()
{
RI i,t=0,P=1;for(scanf("%lld%lld",&n,&m),Init(),i=0;i^Pt;++i)//枚举模数的每个质因子
t+=1LL*(Calc(p[i])-t%p[i]+p[i])*QP(P,p[i]-2,p[i])%p[i]*P,P*=p[i];//中国剩余定理合并
return printf("%d\n",QP(m%X,t,X)),0;
}
待到再迷茫时回头望,所有脚印会发出光芒