Polya计数法总结 POJ2409&&2154&&SGU282

Polya计数法原理可以看符文杰的集训队论文<<Polya原理及其应用>>、题目可以看陈瑜希的论文<<Polya计数法的应用>>

解题三步骤:

1、确定置换群(注意考虑周全)

2、计算每个置换下的循环节数目

3、带入公式

有时候置换数目太多,而许多置换具有相同的循环节数目,可以统一起来考虑,进行优化。

下面是三道例题,难度递增,第一题是裸题,后面两题都用到了上面说的优化。

1、POJ2409

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n,m;
ll GCD(ll a,ll b)
{
    if(b==0)
        return a;
    return GCD(b,a%b);
}
ll Pow(ll a,ll b)
{
    ll ret=1,c=a;
    while(b)
    {
        if(b&1)
            ret*=c;
        c*=c;
        b>>=1;
    }
    return ret;
}
int main()
{
    ll i,j,ans;
    while(cin>>m>>n)
    {
        if(n==0&&m==0)
            break;
        ans=0;
        for(i=0;i<n;++i)
            ans+=Pow(m,GCD(n,i));
        if(n&1)
            ans+=n*Pow(m,n/2+1);
        else
        {
            ans+=n/2*Pow(m,n/2+1);
            ans+=n/2*Pow(m,n/2);
        }
        cout<<ans/(2*n)<<endl;
    }
    return 0;
}


 

2、POJ2154

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
//typedef long long LL;
int Prime[36000]; //一个数n大于sqrt(n)的质因子最多只有一个
bool Flag[36000];
int tot;
void Get_Prime_Table()
{
    tot=0;
    memset(Flag,false,sizeof(Flag));
    int i,j;
    for(i=2;i<36000;i++)
    {
        if(!Flag[i])
        {
            for(j=2*i;j<36000;j+=i)
                Flag[j]=true;
        }
    }
    for(i=2;i<36000;++i)
        if(!Flag[i])
            Prime[tot++]=i;
//    cout<<tot<<endl;
}
int Eular(int n,int p)
{
    int ret=n,i;
    for(i=0;i<tot&&Prime[i]*Prime[i]<=n;++i)
    {
        if(n%Prime[i]==0)
        {
            ret-=ret/Prime[i];
            while(n%Prime[i]==0)
                n/=Prime[i];
        }
    }
    if(n!=1)
        ret-=ret/n;
    return ret%p;
}
int Pow(int a,int b,int p)
{
    int ret=1,c=a%p;
    while(b)
    {
        if(b&1)
            (ret*=c)%=p;
        (c*=c)%=p;
        b>>=1;
    }
    return ret;
}
int main()
{
    int X;
    int N,P,i,ans;
    scanf("%d",&X);
    Get_Prime_Table();
    while(X--)
    {
        scanf("%d %d",&N,&P);
        ans=0;
        for(i=1;i*i<=N;++i)
        {
            if(i*i==N)
            {
//                cout<<i<<endl;
                cout<<Eular(i,P)<<endl;
//                cout<<"................."<<endl;  
                (ans+=Eular(i,P)*Pow(N,i-1,P))%=P;
            }
            else if(N%i==0)
            {
//                cout<<i<<endl;
//                cout<<Eular(i,P)<<endl;
//                cout<<Eular(N/i,P)<<endl;
//                cout<<"....................."<<endl;
                (ans+=Eular(i,P)*Pow(N,N/i-1,P)+Eular(N/i,P)*Pow(N,i-1,P))%=P;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


 

3、SGU282

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
LL Mod;
LL L[55],Tot,K[55],N,M,Num,Div;
LL GCD(LL a,LL b)
{
    if(b==0)
        return a;
    return GCD(b,a%b);
}
LL Pow(LL a,LL b)
{
    LL c=a%Mod,ret=1;
    while(b)
    {
        if(b&1)
            (ret*=c)%=Mod;
        (c*=c)%=Mod;
        b>>=1;
    }
    return ret;
}
void Init()
{
    Tot=0,Num=1;
    memset(K,0,sizeof(K));
    LL i;
    for(i=2;i<=N;++i)
    {
        (Num*=i)%=Mod;
    }
    Div=Pow(Num,Mod-2);
}
LL Fact(LL a)
{
    LL i,ret=1;
    for(i=2;i<=a;++i)
    {
        (ret*=i)%=Mod;
    }
    return ret;
}
LL DFS_Polya(LL remain,LL limit)
{
    if(remain==0)
    {
        LL i,j,t1=1,t2=0;
        memset(K,0,sizeof(K));
        for(i=0;i<Tot;++i)
        {
            (t1*=L[i])%=Mod;
            t2+=L[i]/2;
            K[L[i]]++;
        }
        for(i=1;i<=N;++i)
        {
            (t1*=Fact(K[i]))%=Mod;
        }
        for(i=0;i<Tot;++i)
        {
            for(j=i+1;j<Tot;++j)
            {
                t2+=GCD(L[i],L[j]);
            }
        }
        return (((Num*Pow(t1,Mod-2))%Mod)*Pow(M,t2))%Mod;
    }
    else
    {
        LL i,j,ret=0;
        for(i=1;i<=limit&&i<=remain;++i)
        {
            L[Tot++]=i;
            (ret+=DFS_Polya(remain-i,i))%=Mod;
            Tot--;
        }
        return ret;
    }
}
int main()
{
    scanf("%I64d %I64d %I64d",&N,&M,&Mod);
    Init();
    printf("%I64d\n",(DFS_Polya(N,N)*Div)%Mod);
    return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值