[POJ1286&POJ2154&POJ2409]Polya定理

Polya定理


  L=1/|G|*(m^c(p1)+m^c(p2)+...+m^c(pk))

  G为置换群大小

  m为颜色数量

  c(pi)表示第i个置换的循环节数

  如置换(123)(45)(6)其循环节数为3

 

-------------------------------------------------------------------------------------------

 

 POJ1286&POJ2409

  都是简单的处理串珠子的问题。

  题目中隐藏着3种不同的置换类别。

  1.旋转

    注意不需要顺时针和逆时针分开考虑 因为顺时针旋转k位等同于逆时针旋转(n-k)位。

    另外当旋转了k位时的循环节数为gcd(k,n) 这个略加脑补也可得出。

  2.翻转(对称)

    需要分n的奇偶分开讨论

    当n为奇数时

      只有一种对称,即以每颗珠子与中心的连线所在的直线为对称轴。这个时候循环节数为(n+1)/2。

    当n为偶数时

      有两种对称,一种同奇数时的情况,但是此时相对的两颗珠子所成的直线为同一条

      所以循环节数为n/2+1,情况的数量也要减少一半。

      另一种是以两颗珠子连线的中点与中心连线所在的直线为对称轴。这种情况的循环节数为n/2,情况也是n/2种。

  这两道题的数据范围都很小,所以直接这样处理就可以了。

  

 

program poj1286;
var i,n:longint;
    tot,sum:int64;
    w:array[-1..25]of int64;

function gcd(x,y:longint):longint;
begin
    if y=0 then exit(x) else exit(gcd(y,x mod y));
end;

begin
    w[0]:=1;
    for i:=1 to 23 do w[i]:=w[i-1]*3;
    readln(n);
    while n<>-1 do
    begin
                if n=0 then
                begin
                        writeln(0);
                        readln(n);
                        continue;
                end;
        tot:=1;sum:=w[n];
        for i:=1 to n-1 do
        begin
            inc(tot);
            inc(sum,w[gcd(i,n)]);
        end;
        if odd(n) then
        begin
            inc(tot,n);
            inc(sum,w[(n+1) >> 1]*n);
        end else
        begin
            inc(tot,n >> 1);
            inc(sum,w[n >> 1+1]*n >> 1);
            inc(tot,n >> 1);
            inc(sum,w[n >> 1]*n >> 1);
        end;
        writeln(sum div tot);
        readln(n);
    end;
end.
program poj2409;
var m,n,tot,sum:int64;
    i:longint;

function w(x:longint):int64;
var i:longint;
begin
    w:=1;
    for i:=1 to x do w:=w*m;
end;

function gcd(x,y:longint):longint;
begin
        if y=0 then exit(x) else exit(gcd(y,x mod y));
end;

begin
    readln(m,n);
    while (m<>0)or(n<>0) do
    begin
        tot:=0;sum:=0;
        for i:=1 to n do
        begin
            inc(tot);
            inc(sum,w(gcd(i,n)));
        end;
        if odd(n) then
        begin
            inc(tot,n);
            inc(sum,w((n+1) >> 1)*n);
        end else
        begin
            inc(tot,n >> 1);
            inc(sum,w(n >> 1)* n >> 1);
            inc(tot,n >> 1);
            inc(sum,w(n >> 1+1)* n >> 1);
        end;
        writeln(sum div tot);
        readln(m,n);
    end;
end.

 

----------------------------------------------------------------------------------

 POJ2154

  

  感觉非常坑啊...

  首先觉得这道题挺好的...数据范围非常大

  由于用到gcd的统计所以自然而然想到了欧拉函数

  然后套一下应该就出来了...

  但是为什么我做了这么久...

  有几个需要注意的地方

  1.最后除以|G|在这里也就是n的步骤由于在取模意义下所以会出错,但是很神奇的发现快速幂的底数也都是n(大概这也是题目中长度和颜色数相同的意图吧),只要将次数-1就可以了。

  2.直接这样会TLE,欧拉函数的求解还需要用欧拉线筛来优化。即用已有的质因子来求phi。过程很简单就不多提了。

  值得一提的是最后一次TLE到AC的跨越仅仅是因为一个变量的类型。即ans变量改成longint就可以过了。

  再一次印证了张老师几年前提到的”空间会影响时间“

  另外在这道题中,正巧将前几天学的欧拉函数和欧拉线筛都运用了起来,感觉非常不错。

program poj2154;
const maxn=trunc(sqrt(1000000000));
var t,test,n,tt:longint;
    vis:array[-1..maxn]of boolean;
    p:array[-1..maxn]of longint;

procedure build;
var i,j:longint;
begin
        fillchar(vis,sizeof(vis),true);
        p[0]:=0;
        for i:=2 to maxn do
        begin
                if vis[i] then
                begin
                        inc(p[0]);p[p[0]]:=i;
                end;
                for j:=1 to p[0] do
                begin
                        if i*p[j]>maxn then break;
                        vis[i*p[j]]:=false;
                        if i mod p[j]=0 then break;
                end;
        end;
end;

function phi(x:longint):longint;
var i,ans,tem:longint;
begin
        ans:=x;tem:=x;
    for i:=1 to p[0] do if tem>=p[i]*p[i] then
                //刚开始写成了tem>=p[i]就失去了优化的意义TAT
    begin
                if x mod p[i]=0 then ans:=ans div p[i]*(p[i]-1);
                    //在ans是longint的情况下先乘后除会爆
                while x mod p[i]=0 do x:=x div p[i];
    end else break;
    if x<>1 then ans:=ans div x*(x-1);
                    //这里也涉及到运算顺序的问题
    exit(ans mod tt);
end;

function mul(a,b:longint):longint;
var ans,w:int64;
begin
    ans:=1;w:=a mod tt;
    while b<>0 do
    begin
        if b and 1=1 then ans:=(ans*w) mod tt;
        w:=(w*w) mod tt;
        b:=b >> 1;
    end;
    exit(ans);
end;

function solve:longint;
var i:longint;
    ans:int64;
begin
    ans:=0;
    for i:=1 to trunc(sqrt(n)) do if n mod i=0 then
    begin
        ans:=(ans+phi(n div i)*mul(n,i-1)) mod tt;
        if i*i<>n then ans:=(ans+phi(i)*mul(n,n div i-1)) mod tt;
    end;
    exit(ans);
end;

begin
    readln(test);
        build;
        for t:=1 to test do
    begin
        readln(n,tt);
        writeln(solve);
    end;
end.

 

转载于:https://www.cnblogs.com/mjy0724/p/4402049.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值