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