/*
题目要求:给出两个整数n和p,代表n个珠子,n种颜色,要求不同的项链数,并对结果mod(p)处理。
置换只有旋转一种方式,那么共有n个置换
基本知识:环的个数为gcd(n , i) , 长度L=n / gcd(n , i) 其中 i 为转的位子数
普通求法: ∑n^( gcd(n,i) ) 0<=i<n 复杂度过高
优化:枚举环的长度L
枚举优化: L可以从1取到sqrt(n) ,因为L|n , n/L | n
对于每个L,我们再看有几个i满足条件
n/L = gcd(n , i)
那么令 a=n/L = gcd(n , i) , 再设i = at
那么当且仅当gcd(L,t)=1时候,才有gcd(n,i) = a
显然满足条件的i的个数就是t的个数也就是phi(L)
那么最后统计一下就是 ∑(phi(L) * N^(L-1) ) % p (L即枚举值)
*/
#include<iostream>
#include <stdlib.h>
#include <algorithm>
#include <stdio.h>
#include<vector>
using namespace std;
const int MAXN=1000000000;
int isprime[50001];
int prime[8001];
int num,n,p;
void getprime()
{
num=0;
for(int i=2;i<=50000;i++)if(!isprime[i])
{
prime[num++]=i;
for(int j=1;j*i<=50000;j++)
{
isprime[i*j]=1;
}
}
}
int euler(int x)
{
int res=x;
for(int i=0;i<num&&prime[i]*prime[i]<=x;i++)
{
if(x%prime[i]==0)
{
res=res/prime[i]*(prime[i]-1);
while(x%prime[i]==0)
{
x/=prime[i];
}
}
}
if(x>1) res=res/x*(x-1);
return res;
}
int expmod(int a,int b,int mod)
{
int ret=1;
a=a%mod;
while(b>0)
{
if(b&1)ret=(ret*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ret;
}
int main()
{
int Case;
getprime();
scanf("%d",&Case);
while(Case--)
{
scanf("%d%d",&n,&p);
int ans=0,i;
for(i=1;i*i<n;i++)if(n%i==0)
{
ans=(ans+euler(i)%p*expmod(n,n/i-1,p)+euler(n/i)%p*expmod(n,i-1,p))%p;;//这里的i-1代表已经除以整个置换数n了,原本是expmod(n,i),最后要除以n的,
}
if(i*i==n)
ans=(ans+euler(i)*expmod(n,i-1,p))%p;
cout<<ans<<endl;
}
return 0;
}
POJ 2154 Color Polya定理+欧拉函数
最新推荐文章于 2021-02-26 21:51:37 发布