QOJ1285 Stirling Number

QOJ 传送门

因为 xp¯xpx(modp),所以设 n=pq+r,其中 r[0,p1],则有:

xn¯=(i=0q1(x+ip)p¯)(x+pq)r¯=(xpx)q(x+pq)r¯=(xpx)qxr¯=xq(xp11)qxr¯

kq=a(p1)+b,其中 b[0,p2],那么:

[nk]=[xk]xn¯=[xk]xq(xp11)qxr¯=[xkq](xp11)qxr¯=(1)qa(qa)[rb]

mq=k(p1)+t,其中 t[0,p2],那么:

i=0m[ni]=i=0t[ri]j=0k(1)qj(qj)+i=t+1r[ri]j=0k1(1)qj(qj)

又因为:

i=0m(1)i(ni)=(1)m(n1m)

这个式子由 (nm)=(n1m)+(n1m1) 易证。

那么:

i=0m[ni]=i=0t[ri](1)qk(q1k)+i=t+1r[ri](1)qk+1(q1k1)=i=0t[ri](1)qk(q1k)+i=0r[ri](1)qk+1(q1k1)+i=0t[ri](1)qk(q1k1)=(1)qk((qk)i=0t[ri](q1k1)i=0r[ri])=(1)qk((qk)i=0t[ri](q1k1)r!)

那么剩下的问题是给定 r,t,求 xr¯ 的前 t 次项系数和。

考虑一个多项式 f(x),设模数的原根为 g,若我们求出 f(g0),f(g1),,f(gp2),那么我们可以线性地求出 f(x) 的前 t 次项系数和。

具体地,考虑 i=0p2gki,只有当 k=0 时这个式子等于 p11(modp),当 k0 时由等比数列求和公式可知它等于 0

那么考虑若要求 f(x) 的第 j 次项,i=0p2gijf(gi) 即为答案。

所以:

i=0t[xi]xr¯=i=0tj=0p2gijf(gj)=j=0p2f(gj)(i=0tgij)

f(gi) 可以预处理阶乘及其逆元然后 O(1) 单次计算,后面那个 i=0tgij 可以用等比数列求和公式然后预处理原根的次方做到 O(1) 单次计算。

总时间复杂度 O(p)

code
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 1000100;
ll n, L, R, P, fac[maxn], ifac[maxn], N, G, q, r, inv[maxn], pw[maxn];
inline ll qpow(ll b, ll p) {
ll res = 1;
while (p) {
if (p & 1) {
res = res * b % P;
}
b = b * b % P;
p >>= 1;
}
return res;
}
inline ll C(ll n, ll m) {
if (n < m || n < 0 || m < 0) {
return 0;
}
if (n >= P) {
return C(n / P, m / P) * C(n % P, m % P) % P;
} else {
return fac[n] * ifac[m] % P * ifac[n - m] % P;
}
}
inline bool check(ll x) {
ll t = P - 1;
vector<ll> vc;
for (ll i = 2; i * i <= t; ++i) {
if (t % i == 0) {
vc.pb(i);
while (t % i == 0) {
t /= i;
}
}
}
if (t > 1) {
vc.pb(t);
}
for (ll y : vc) {
if (qpow(x, (P - 1) / y) == 1) {
return 0;
}
}
return 1;
}
inline ll g(ll n, ll m) {
ll p = 1, ip = 1, iG = qpow(G, P - 2), res = 0;
for (int i = 0; i <= P - 2; ++i, p = p * G % P, ip = ip * iG % P) {
if (p + n - 1 >= P) {
continue;
}
if (ip == 1) {
res = (res + (m + 1) * fac[p + n - 1] % P * ifac[p - 1]) % P;
} else {
res = (res + (pw[((-(m + 1) * i) % (P - 1) + P - 1) % (P - 1)] + P - 1) % P * inv[(ip + P - 1) % P] % P * fac[p + n - 1] % P * ifac[p - 1]) % P;
}
}
return (P - res) % P;
}
inline ll h(ll m) {
if (m < q) {
return 0;
}
ll k = (m - q) / (P - 1);
ll t = (m - q) % (P - 1);
ll ans = C(q, k) * g(r, t) % P;
ans = (ans - C(q - 1, k - 1) * fac[r] % P + P) % P;
if ((q - k) & 1) {
ans = (P - ans) % P;
}
return ans;
}
void solve() {
scanf("%lld%lld%lld%lld", &n, &L, &R, &P);
N = P - 1;
fac[0] = 1;
for (int i = 1; i <= N; ++i) {
fac[i] = fac[i - 1] * i % P;
}
ifac[N] = qpow(fac[N], P - 2);
for (int i = N - 1; ~i; --i) {
ifac[i] = ifac[i + 1] * (i + 1) % P;
}
inv[1] = 1;
for (int i = 2; i <= N; ++i) {
inv[i] = (P - P / i) * inv[P % i] % P;
}
for (G = 1;; ++G) {
if (check(G)) {
break;
}
}
pw[0] = 1;
for (int i = 1; i <= P - 2; ++i) {
pw[i] = pw[i - 1] * G % P;
}
q = n / P;
r = n - P * q;
printf("%lld\n", (h(R) - h(L - 1) + P) % P);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
posted @   zltzlt  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示