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

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值