poj2154

来源:互联网 发布:c语言制表符 编辑:程序博客网 时间:2024/05/19 20:37

【题意】

将正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.