poj 2154 Color(欧拉函数,快速幂,波利亚计数)

本文详细介绍了如何使用数学方法计算不同颜色圆珠手链的种类数量,涉及快速幂、欧拉函数等高效算法,适用于大规模数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Color
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 7458 Accepted: 2452

Description

Beads of N colors are connected together into a circular necklace of N beads (N<=1000000000). Your job is to calculate how many different kinds of the necklace can be produced. You should know that the necklace might not use up all the N colors, and the repetitions that are produced by rotation around the center of the circular necklace are all neglected. 

You only need to output the answer module a given number P. 

Input

The first line of the input is an integer X (X <= 3500) representing the number of test cases. The following X lines each contains two numbers N and P (1 <= N <= 1000000000, 1 <= P <= 30000), representing a test case.

Output

For each test case, output one line containing the answer.

Sample Input

5
1 30000
2 30000
3 30000
4 30000
5 30000

Sample Output

1
3
11
70
629

本题只考虑旋转,不考虑翻转,因此置换群大小为N!ans = [N^gcd(1,N)+N^gcd(2,N)...+N^gcd(N,N)]/N;
=>ans = N^(gcd(1,N)-1)+N^(gcd(2,N)-1)...+N^(gcd(N,N)-1)这个时候我们就可以在运算时%P了。

难点1:如何提高求gcd(i,N)的效率?
    我们知道,如果用暴力的话肯定是超时的,因为N<=1000000000!
    但是,gcd(i,N)的数量是很少的,我们可以枚举,怎么枚举呢?只要枚举从[1,sqrt(N)]就行了,因为求出所有i∈[1,sqrt(N)]能够满足N%i==0,就可以求出所有j∈[sqrt(N),N]能够满足N%j==0,j=N/i。
    这样枚举的效率是sqrt(N)。ans = ∑sum[L]*N^(L-1),sum[L]为gcd(i,N)==L的个数。

难点2:如何求出gcd(i,N)==L时,i的个数?
    i=L*x , N=L*y,因为gcd(i,N)==L,所以x,y互质。反证法,如果x,y不互质,则gcd(i,N)!=L。
    因此,枚举L就可以求出y,然后求[1,y]与y互质的个数,这个可以用欧拉函数解决!
    欧拉函数的效率小于是sqrt(y)*log2(y).

难点3:如何求N^(gcd(i,N)-1)?
    如果用库函数pow会有精度损失,这肯定是不行的,如果自己用暴力写肯定会超时的,因为gcd(i,N)最大是1000000000。
    所以我们用快速幂求,效率是log2(N)<30.

卡过!

#include <iostream>
#include <cstdio>
using namespace std;

#define ll long long
ll N , P;

ll pow(ll C , ll t){//快速幂求C^t
	ll ans = 1;
	while(t>0){
		if(t%2==1){
			ans = (ans*C)%P;
		}
		C = (C%P*(C%P))%P;
		t /= 2;
	}
	return ans%P;
}

ll Euler(int x){
	ll ans = 1;
	for(int i = 2; i*i <= x; i++){
		if(x%i == 0){
			ans = ans*(i-1)%P;
			x /= i;
			while(x%i==0){
				ans = ans*i%P;
				x /= i;
			}
		}
	}
	if(x > 1) ans = (ans%P)*((x-1)%P);
	return ans;
}

ll Polya(int Bean , int Color){//波利亚计数,Bean表示项链珠子个数,Color表示颜色种数
	ll ans = 0;
	for(int i = 1; i*i <= Bean; i++){
		if(Bean%i == 0){
			if(i != Bean/i){
				ans += (Euler(i)%P*pow(Color , Bean/i-1)%P)%P;
				ans += (Euler(Bean/i)%P*pow(Color , i-1)%P)%P;
			}else{
				ans += (Euler(i)%P*pow(Color , Bean/i-1)%P)%P;
			}
			ans = ans%P;
		}
	}
	return ans%P;
}
int main(){
	int T;
	cin >> T;
	while(T--){
		scanf("%lld%lld" , &N , &P);
		printf("%lld\n" , Polya(N , N));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值