首先这个 \(C(i, j)\bmod j\) 的形式就非常怪,于是首先肯定要先研究一下这个值。
先考虑如何求 \(C(i, j)\)。
可以考虑先选出要用的 \(j\) 个数,再乘上其排列成环的方案数,那么有 \(C(i, j) = \binom{i}{j}\times (j - 1)!\)。
那么就是来考虑 \(\binom{i}{j}\times (j - 1) ! \bmod j\) 的值了。
注意到后面的这个 \((j - 1)! \bmod j\) 是个定值,于是考虑分析这个值。
首先若 \(j\in \mathbb{P}\),根据威尔逊定理那么有 \((j - 1)!\bmod j = j - 1\)。
否则若 \(j = 1\),显然为 \(0\)。
接下来就考虑 \(j\) 为合数的情况了,感受一下会觉得其实这些值大部分都为 \(0\)。
考虑令 \(j = x\times p^k\),那么 \((j - 1)!\bmod j \not = 0\) 就肯定要使得 \(k\ge x\times p^{k - 1}\not = 1\)(\(= 1\) 就为质数了)。
那么就能发现只可能在取 \(x = 1, p = 2, k = 2\) 时有解,即 \(j = 4\) 时。
那么对于 \(j = 4\),显然是最特殊的,但是考虑到也只有这一种 \(j\),所以直接考虑直接预处理 \(\binom{i}{4}\times 6 \bmod 4\) 然后前缀和。
那么就只需要考虑 \(j\in \mathbb{P}\) 的情况了。
那么此时化简一下就有 \(\binom{i}{j}\times (j - 1)! \bmod j = \binom{i}{j}\times (j - 1)\bmod j = (j\binom{i}{j} - \binom{i}{j})\bmod j = (j - \binom{i}{j}\mod j)\mod j\)。
于是问题来到如何求 \(\binom{i}{j}\bmod j\)。
注意到此时 \(j\in \mathbb{P}\),那么就可以用 Lucas 定理了。
此时 \(\binom{i}{j}\bmod j = \binom{\lfloor\frac{i}{j}\rfloor}{\lfloor\frac{j}{j}\rfloor}\binom{i\bmod j}{j\bmod j} = \binom{\lfloor\frac{i}{j}\rfloor}{1}\binom{i\bmod j}{0} = \lfloor\frac{i}{j}\rfloor\times 1 = \lfloor\frac{i}{j}\rfloor\)。
于是 \(C(i, j) = (j - \lfloor\frac{i}{j}\rfloor)\bmod j\)。
注意到 \(x\in [kj, (k + 1)j)\) 都有 \(C(x, j) = (j - k\bmod j)\bmod j\),这说明实际上可以把这些 \(k\) 相同的 \(x\) 用差分来处理,就只需要对于每个 \(k\) 处理一次了。
那么这个 \(k\) 的上限就是 \(\lceil\frac{n}{j}\rceil\)。
那么复杂度就是与埃氏筛相同的 \(\mathcal{O}(n\log \log n)\)。
最后总时间复杂度 \(\mathcal{O}(n\log \log n + t)\)。
#include<bits/stdc++.h>
constexpr int mod = 1e9 + 7;
constexpr int maxn = 1e6 + 10, N = 1e6;
int a[maxn], vis[maxn];
inline void init() {
for (int i = 2; i <= N; i++) {
if (vis[i]) continue;
for (int j = 0, k = 0; j <= N; j += i, k = (i + k - 1) % i) {
vis[j] = 1;
(a[j] += k) %= mod, (a[std::min(N + 1, j + i)] += mod - k) %= mod;
}
}
for (int i = 1; i <= N; i++) {
(a[i] += a[i - 1]) %= mod;
}
for (int i = 1; i <= N; i++) {
(a[i] += ((__int128_t)i * (i - 1) * (i - 2) * (i - 3) / 4) % 4) %= mod;
}
for (int i = 1; i <= N; i++) {
(a[i] += a[i - 1]) %= mod;
}
}
inline void solve() {
int n;
scanf("%d", &n);
printf("%d\n", a[n]);
}
int main() {
init();
for (int T, _ = scanf("%d", &T); T--; ) {
solve();
}
return 0;
}
标签:lfloor,1957E,frac,bmod,Solution,rfloor,times,Combinations,binom
From: https://www.cnblogs.com/rizynvu/p/18550983