Codeforces Round 940 E. Carousel of Combinations 【威尔逊定理】

文章介绍了如何计算给定正整数n的圆排列中,选取不同数的方案数C(i,j)对j取模的结果,利用了排列理论、威尔逊定理以及合数与质数对结果的影响,最终通过欧拉筛优化算法求解,时间复杂度为O(nlogn)。
摘要由CSDN通过智能技术生成

E

题意

给定一个正整数 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=1nj=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(i1)...(ij+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,j1] 都会出现,那么现在就变成:
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=((j1)!×ji) mod j j j

至此,就出现了经典的威尔逊定理的格式:

p p p质数时, ( p − 1 ) ! (p - 1)! (p1)! mod p = p − 1 p = p - 1 p=p1

同时,当 p p p大于 4 4 4合数时, ( p − 1 ) ! (p - 1)! (p1)! mod p = 0 p = 0 p=0,因为前 p − 1 p-1 p1 个数乘积一定会出现 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 (41)!=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=jnji 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; 
}
  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值