传送门
【分析】
先推一波公式:
答案 res 显然有公式:(其中 Dn 表示 n 个元素全部错排的方案数)
res=n!1x=0∑n(xn)xkDn−x=x=0∑nx!xk⋅(n−x)!Dn−x
由于错排问题有公式:Dn=⌊en!⌋=n!⋅i=0∑ni!(−1)i
因此答案为:res=x=0∑nx!xk⋅i=0∑n−xi!(−1)i
对 xk 用第二类斯特林数展开为下降幂,得到:
res=x=0∑nx!xk⋅i=0∑n−xi!(−1)i=t=0∑k{kt}x=0∑nx!xt⋅i=0∑n−xi!(−1)i=t=0∑min(n,k){kt}x=0∑n−tx!1⋅i=0∑(n−t)−xi!(−1)i=t=0∑min(n,k){kt}T=0∑n−ti+j=T∑j!1⋅i!(−1)i=t=0∑min(n,k){kt}[1+T=1∑n−tT!1i+j=T∑(i,jT)(−1)i]=t=0∑min(n,k){kt}[1+T=1∑n−tT!1⋅(1−1)T]=t=0∑min(n,k){kt}=Bellk−t=n+1∑k{kt}
现在的问题化为,如何求解出 Bellk 和 {kt}
关于前者,根据题解提到的 Touchard 同余,可以得到贝尔数在模质数 p 意义下,有 Belln≡Belln−p+Belln−(p−1)(modp)
这显然是一个 p 阶线性递推的形式,可以在 O(p2logk) 的时间内求解出 Bellkmodp 的答案
而关于第二类斯特林数,有公式:
{xx−n}=k=0∑n−1⟨⟨nk⟩⟩(2nx+n−k−1)
其中,⟨⟨nk⟩⟩ 为第二类欧拉数,或二阶欧拉数。它表示多重集 {1,1,2,2,⋯,n,n} 构成的序列中,任意两个相同的 i 之间,所有数字都大于 i 的序列方案数。如 123321 是满足要求的,但 123123 不是,因为两个 2 之间含有小于 2 的 1 ;两个 3 之间含有小于 3 的 1,2
第二类欧拉数有递推公式: ⟨⟨nk⟩⟩=(2n−k−1)⟨⟨n−1k−1⟩⟩+(k+1)⟨⟨n−1k⟩⟩
因此,模数为小质数 p 时,我们可以采用 O(p2logk) 的时间求解 Bellkmodp ;O(p2) 递推 (mn)(0≤n,m<p) 的数值,然后再在 O((k−n)2) 的时间内地推出所有 O(k−n) 的第二类欧拉数,最后对于每个 {kt} 利用第二类欧拉数和 Lucas 定理进行 O((k−t)logk) 求解
总复杂度为 O(p2logk)+O(p2)+O((k−n)2)+t=n+1∑kO((k−t)logk)=O(p2logk+(k−n)2logk)
然而,我们的模数 862118861=857×997×1009 是个合数,不适用上面的算法。这当然很好解决,我们单独跑每个质数的结果,最后用中国剩余定理合并即可
【代码】
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
#define de(x) cout << #x <<" = "<<(x)<<endl
#define dd(x) cout << #x <<" = "<<(x)<<" "
#define all(a) a.begin(), a.end()
#define rep(i, a, b) for(int i=(a); i<(b); ++i)
#define per(i, a, b) for(int i=(b)-1; i>=(a); --i)
const int P=862118861, p[]={0, 857, 997, 1009};
ll n, k, S[1024][1024], Bell[1024];
inline ll kpow(ll a,ll x, ll p) { ll ans=1; for(;x;x>>=1, a=a*a%p) if(x&1) ans=ans*a%p; return ans; }
inline ll inv(ll a, ll p) { return kpow(a, p-2, p); }
int linear_recurrence(ll n, int m, vi a, vi c, int P) {
if (n<m) return (a[n]+P)%P;
vector<ll> v(m, 0), u(m<<1, 0);
v[0] = 1;
for(ll x = 0, W = n ? 1ll<<(63 - __builtin_clzll(n)) : 0; W; W >>= 1, x <<= 1) {
fill(all(u), 0);
int b = !!(n & W); if(b) x++;
if(x < m) u[x] = 1;
else {
rep(i, 0, m) rep(j, 0, m) (u[i + b + j] += v[i] * v[j]) %= P;
per(i, m, 2*m) rep(j, 0, m) (u[i - m + j] += c[j] * u[i]) %= P;
}
copy(u.begin(), u.begin() + m, v.begin());
}
ll ans = 0;
rep(i, 0, m) (ans += v[i] * a[i]) %= P;
return (ans+P)%P;
}
vi linear_a, linear_c;
inline int work_bell(ll k, int p) {
linear_a.clear();
linear_c.clear();
S[0][0]=1;
Bell[0]=1;
linear_a.push_back(Bell[0]);
for(int i=1; i<=p; ++i) {
for(int j=1; j<=i; ++j)
S[i][j]=(S[i-1][j-1]+(ll)S[i-1][j]*j)%p;
Bell[i]=0;
for(int j=1; j<=i; ++j)
Bell[i]=(Bell[i]+S[i][j])%p;
linear_a.push_back(Bell[i]);
}
linear_c.resize(p);
linear_c[0]=linear_c[1]=1;
return linear_recurrence(k, p, linear_a, linear_c, p);
}
int Euler2[5010][5010], C[1024][1024];
inline void pre_Euler2(int p) {
Euler2[0][0]=1;
for(int n=1; n<=5000; ++n) {
Euler2[n][0]=1;
for(int k=1; k<=n; ++k)
Euler2[n][k]=((2ll*n-k-1)*Euler2[n-1][k-1]+(k+1ll)*Euler2[n-1][k])%p;
}
}
inline void pre_C(int p) {
C[0][0]=1;
for(int i=1; i<p; ++i) {
C[i][0]=C[i][i]=1;
for(int j=1; j<i; ++j)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
}
}
inline int Lucas(ll n, ll m, int p) {
int res=1;
for(; n||m; n/=p, m/=p)
res=(ll)res*C[n%p][m%p]%p;
return res;
}
inline int S2(ll x, int n, int p) {
int res=0;
for(int k=0; k<=n; ++k)
res=(res+(ll)Euler2[n][k]*Lucas(x+n-k-1, n+n, p))%p;
return res;
}
inline int work(int p) {
int res=work_bell(k, p);
pre_Euler2(p);
pre_C(p);
for(ll t=n+1; t<=k; ++t)
res=(res-S2(k, k-t, p))%p;
return res<0?res+p:res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>k;
int x1=work(p[1]);
int x2=work(p[2]);
int x3=work(p[3]);
ll m1=P/p[1], m2=P/p[2], m3=P/p[3];
ll res=x1*m1*inv(m1, p[1])+x2*m2*inv(m2, p[2])+x3*m3*inv(m3, p[3]);
cout<<res%P;
cout.flush();
return 0;
}