【题意】
将正n边形的n个顶点用n种颜色染色,问有多少种方案(答案mod p,且可由旋转互相得到的算一种)
【输入】
第一行一个数字x(x<=3500)表示数据组数
接下来x行每行两个数字表示n(n<=1000000000)和p(p<=30000)
【输出】
对于每组数据,输出一行表示答案
波利亚定理的应用
因为只有旋转,所以置换有n种
枚举种比较慢,复杂度为n log n 明显超时
所以枚举循环节长度l
那么循环节个数为a=n/l
设偏移个数i=ka
因为循环长度为l,所以gcd(n,i)=a
同除a得
gcd(n/a,i/a)=1,即gcd(l,k)=1
所以对l求欧拉函数即可得到k的个数
这道题的时间要求十分紧俏,所以数据类型最好用longint,因为p<=30000,并不会溢出
求欧拉函数的时候,最好预处理一下,把n分解质因数
program poj2154;
var
left,ans,n,p,m:longint;
tot,x,i,j,k,l:longint;
prime:array [0..10001] of longint;
xxx:array [0..32] of longint;
function quick (a,b:longint):longint;
var
k:longint;
i,ans:longint;
begin
if b=0 then exit(1);
ans:=1;
while b>0 do
begin
k:=trunc(ln(b)/ln(2));
ans:=ans*xxx[k] mod p;
b:=b-(1 shl k);
end;
exit(ans);
end;
function euler (n:longint):longint;
var
ans,i,j,k:longint;
begin
ans:=n;
for i:=1 to tot do
if prime[i]>n then break
else
if n mod prime[i] = 0 then ans:=ans div prime[i] * (prime[i]-1);
exit(ans mod p);
end;
begin
read(x);
while x>0 do
begin
dec(x);
read(n,p);
left:=n;
tot:=0;
for i:=2 to trunc(sqrt(n)) do
if left mod i = 0 then
begin
inc(tot);
prime[tot]:=i;
while left mod i = 0 do
left:=left div i;
end;
if left>1 then
begin
inc(tot);
prime[tot]:=left;
end;
xxx[0]:=n mod p;
for i:=1 to 32 do
xxx[i]:=xxx[i-1]*xxx[i-1] mod p;
ans:=0;
for l:=1 to trunc(sqrt(n)) do
if n mod l = 0 then
if l*l=n then
ans:=(ans+euler(l)*quick(n,n div l - 1) mod p) mod p
else
ans:=(ans+euler(l)*quick(n,n div l - 1) mod p
+euler(n div l)*quick(n,l - 1) mod p) mod p;
writeln(ans);
end;
end.