题目地址:http://poj.org/problem?id=2154
题意:给出两个整数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即枚举值)
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <list>
#include <deque>
#include <queue>
#include <iterator>
#include <stack>
#include <map>
#include <set>
#include <algorithm>
#include <cctype>
using namespace std;
typedef __int64 LL;
const int N=1;
//const LL mod=1000000007LL;
const int INF=0x3f3f3f3f;
const double PI=acos(-1.0);
int mod;
int powmod(int a,int b)
{
int ans=1;
a=a%mod;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int euler(int n)
{
int ans=n,m=sqrt(n+0.5);
for(int i=2;i<=m;i++)
if(n%i==0)
{
ans=ans/i*(i-1);
while(n%i==0)
n/=i;
m=sqrt(n+0.5);
}
if(n>1)
ans=ans/n*(n-1);
return ans;
}
int main()
{
int i,j,t,ca=0;
scanf("%d",&t);
while(t--)
{
int n,c,p;
scanf("%d%d",&n,&mod);
c=n;
int xh=0;
for(i=1;i*i<n;i++ )
if(n%i==0)
{
xh=(xh+euler(i)%mod*powmod(c,n/i-1))%mod;
xh=(xh+euler(n/i)%mod*powmod(c,i-1))%mod;
//这里的i-1代表已经除以整个置换数n了,原本是powmod(n,i),最后要除以n的,
}
if(i*i==n)
xh=(xh+euler(i)*powmod(c,i-1))%mod;
printf("%d\n",xh);
}
return 0;
}
/*
5
1 30000
2 30000
3 30000
4 30000
5 30000
*/