题意
给定一个正整数 n n n,定义 C ( i , j ) C(i, j) C(i,j) 为:从 ( 1 , 2 , 3 , . . . , i ) (1,2,3,...,i) (1,2,3,...,i) 中选出 j j j 个不同的数,构成一个圆排列的不同的方案数
求出: ∑ i = 1 n ∑ j = 1 i ( C ( i , j ) m o d j ) \sum_{i=1}^{n} \sum_{j = 1}^{i} (C(i,j) mod \hspace{3pt} j) ∑i=1n∑j=1i(C(i,j)modj)
思路
首先 C ( i , j ) = A i j j C(i,j) = \dfrac{A_{i}^{j}}{j} C(i,j)=jAij,因为普通的排列其实就可以看成是从原排列中的 j j j 个数,选择一个作为 开头 元素,即 A i j = C ( i , j ) × j A_{i}^{j} = C(i,j) \times j Aij=C(i,j)×j
因此
C
(
i
,
j
)
C(i,j)
C(i,j) mod
j
=
i
(
i
−
1
)
.
.
.
(
i
−
j
+
1
)
j
j = \dfrac{i(i - 1)...(i - j + 1)}{j}
j=ji(i−1)...(i−j+1) mod
j
j
j,注意到分子是连续的
j
j
j 个数,因此分子的
j
j
j 个数中,一定至少有一个数是
j
j
j 的倍数,这个数就是
j
×
⌊
i
j
⌋
j \times \lfloor \dfrac{i}{j} \rfloor
j×⌊ji⌋,那么我们不妨用这个数去和分母作除法,剩下的其他数字模
j
j
j 就是
[
1
,
j
−
1
]
[1,j-1]
[1,j−1] 都会出现,那么现在就变成:
C
(
i
,
j
)
C(i,j)
C(i,j) mod
j
=
(
(
j
−
1
)
!
×
⌊
i
j
⌋
)
j = \left( (j-1)! \times \lfloor \dfrac{i}{j} \rfloor \right)
j=((j−1)!×⌊ji⌋) mod
j
j
j
至此,就出现了经典的威尔逊定理的格式:
当 p p p 为质数时, ( p − 1 ) ! (p - 1)! (p−1)! mod p = p − 1 p = p - 1 p=p−1
同时,当 p p p 为大于 4 4 4 的合数时, ( p − 1 ) ! (p - 1)! (p−1)! mod p = 0 p = 0 p=0,因为前 p − 1 p-1 p−1 个数乘积一定会出现 p p p 的倍数
因此当
j
>
4
j > 4
j>4 且为合数时,
C
(
i
,
j
)
=
0
C(i,j) = 0
C(i,j)=0
当
p
p
p 为质数时,
C
(
i
,
j
)
=
−
⌊
i
j
⌋
C(i,j) = -\lfloor \dfrac{i}{j} \rfloor
C(i,j)=−⌊ji⌋ mod
j
j
j
当
p
=
4
p = 4
p=4 时,由于
(
4
−
1
)
!
=
6
(4-1)! = 6
(4−1)!=6 mod
4
=
2
4 = 2
4=2,最后和
⌊
i
4
⌋
\lfloor \dfrac{i}{4} \rfloor
⌊4i⌋ 乘起来对
4
4
4 求模,是以
0
0
0 和
2
2
2 为值,周期为
4
4
4 交替,其实也是类似质数
不妨把
j
j
j 交换到循环外层
对于当前质数
p
p
p,它对答案的贡献是
∑
i
=
j
n
−
⌊
i
j
⌋
\sum_{i = j}^{n} -\lfloor \dfrac{i}{j} \rfloor
∑i=jn−⌊ji⌋ mod
j
j
j,不难发现,这个值是以
j
j
j 为周期变化的,因为是下取整且取模,那么就可以将
[
i
,
n
]
[i,n]
[i,n] 分成长度为
j
j
j 的若干段,分段计算,一共
O
(
n
j
)
O(\dfrac{n}{j})
O(jn) 段
那么对于所有质数,总复杂度为: O ( n log n ) O(n \log n) O(nlogn) (调和级数复杂度)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
const int N = 1000001;
const ll mod = 1e9 + 7;
std::vector<int> primes;
bool vis[N + 5];
ll ans[N + 5];
ll d[N + 5];
void init(){ //欧拉筛
fore(i, 2, N){
if(!vis[i]) primes.push_back(i);
for(auto p : primes){
if(p * i >= N) break;
vis[p * i] = true;
if(i % p == 0) break;
}
}
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
init();
/* 处理质数 */
for(auto p : primes){
for(int l = p; l < N; l += p){
int r = std::min(l + p - 1, N - 1);
ll w = -l / p;
w %= p;
if(w < 0) w += p;
d[l] = (d[l] + w) % mod;
d[r + 1] = (d[r + 1] - w + mod) % mod;
}
}
/* 单独处理 j = 4 的情况 */
int p = 4;
for(int l = p; l < N; l += p){
int r = std::min(l + p - 1, N - 1);
ll w = l / p;
w *= -2;
w %= p;
if(w < 0) w += p;
d[l] = (d[l] + w) % mod;
d[r + 1] = (d[r + 1] - w + mod) % mod;
}
fore(i, 1, N){
d[i] = (d[i - 1] + d[i]) % mod;
ans[i] = (ans[i - 1] + d[i]) % mod;
}
int t;
std::cin >> t;
while(t--){
int n;
std::cin >> n;
std::cout << ans[n] << endl;
}
return 0;
}