poj 2154

polya计数+eular函数

输入n,p;(n<=1e9,p<=30000);n个珠子行成一个圆链,有n种颜色的珠子,各种颜色都为无穷大,旋转后一样的珠子为同一种,求不同链子的总数。
听起来就是普通的polya计数,不过一看n=1e9,太大了没办法枚举。
先说一下polya计数:涉及置换群,m种颜色涂染n个物体。
不同染色方案数L=(m^c(g1)+m^c(g2)+..+m^c(gs))/|G| gs为置换的方法,这题就是旋转1,2..n格,c^(g1)为循环节数,即置换后形成的环的个数;|G|为置换方法总数;这里为n。

然后直接给结论吧:
旋转:旋转k个位置时循环节数为gcd(n,k);   m^c(gs)=m^gcd(n,k);
翻转:n为奇数时,共有n个循环节数为n/2+1的循环群;
     n为偶数时,共有n/2个循环节数为n/2+1的循环群,n=0,还是0;此时|G|=2*n;
     不过这题不考虑翻转的情况。


回到题目,由上可知,这时要旋转n个格即有n种置换方法,有1e9种方法,枚举超时;
我们再看一下计算公式:L=(n^gcd(n,1)+n^gcd(n,2)+..+n^gcd(n,n));枚举k共要n次,可
认真看一下,我们可以知道,gcd的值的种数是比较小的<=n的因子个数,gcd必定是n的因子。
然后我们开始想办法枚举gcd值,并计算各个gcd值的个数。
已知n=gcd*x,k=gcd*y;x,y必互质,否知gcd并不是真的最大公约数。所以1<=y<=x;即gcd(n,k)中为gcd值的
个数即为y的种数,即欧拉函数值eular(x),1<=y<=x的中与x互质的个数。
得出这个结论后就可以枚举gcd值了,ans+=pow(n,gcd)*eular(n/gcd=x);有gcd必有n/gcd,
pow(n,n/gcd)*eular(gcd),然后枚举到n^1/2即可。结果要模。
注:题中pow(n,gcd-1)是pow(n,gcd)/|G|,|G|提前除去了。。

#include<stdio.h>
#include<string.h>
#include<math.h>
#define N 50050
#define M 1e9
int num[N],prime[N],k=0;
int quickpow(int m,int n,int k)
{
    int b=1;
    m=m%k;
    while (n>0)
    {
        if (n&1) b=(b*m)%k;
        n>>=1;
        m=(m*m)%k;
    }
    return b%k;
}
int eular(int n,int mod)
{
    int i=0,ans=1;
    for (i=0;prime[i]*prime[i]<=n;++i)
    {
        if (n%prime[i]!=0)continue;
        ans*=prime[i]-1;
        n/=prime[i];
        while (n%prime[i]==0)
        {
            ans*=prime[i];
            n/=prime[i];
        }
    }
    if (n>1) ans*=(n-1);
    return ans%mod;
}
int main ()
{
    int x,n,p,ans;
    int i,j;
    memset(num,0,sizeof(num));
    for (i=2;i<=1000;++i)
    for (j=i;i*j<=50000;++j)
        num[i*j]=1;
    for (i=2;i<50000;++i)
        if (!num[i])prime[k++]=i;
    scanf("%d",&x);
    while (--x>=0)
    {
        ans=0;
        scanf("%d%d",&n,&p);
        for (i=1;i*i<=n;++i)
        {
            if (i*i==n) ans+=(quickpow(n,i-1,p)*eular(i,p))%p;
            else
            if (n%i==0)
                ans+=(quickpow(n,n/i-1,p)*eular(i,p)+quickpow(n,i-1,p)*eular(n/i,p))%p;
            ans%=p;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/summer-hikaru/archive/2012/11/26/2788412.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值