poj2154

【题意】

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值