HDU 3923 Invoker 【裸Polya 定理】

参考了http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove 的模板

对于每一种染色,都有一个等价群,例如旋转,翻转等。我们将每一种变换转换成一个置换群,通过置换群得到的都是等价的染色方案

最终我们要求的是非等价的染色方案数。

在Burnside定理中给出,在每一种置换群也就是等价群中的数量和除以置换群的数量,即非等价的着色数等于在置换群中的置换作用下保持不变的着色平均数。

我们以POJ 2409 Let it Bead为例http://poj.org/problem?id=2409

N个物品的环,M种颜色。非常基础的问题

那么总共有多少个置换群呢,显然旋转是等价的,那么包括旋转0度,总共便是N个旋转。

对于每一个旋转的等价数量怎么算呢。我们发现每一个旋转置换中有若干个循环节,例如1,3,5是一个循环节,那么旋转一次,1号位到了3号位,3号位到了5号位,显然一个循环节内的颜色一定是一样的。有M种颜色选择,问题转换成有多少个循环节。对于旋转显然有GCD(i,n)。那只要枚举i便可以,这就是Polya定理。

而在翻转当中,如果N为奇数,明显有一个物品是不动的,其它的两两对称,颜色也是一样的。n/2+1个循环节,

如果 N为偶数,分为两种情况,一种是对称轴不过物品,那么所有物品两两对应,n/2个循环节,另外一种是对称轴经过两个物品,(n-2)/2+1+1个循环节。对于每一个循环节有M种颜色可以选择。

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100005
#define inf 1<<29
#define MOD 2007
#define LL long long
using namespace std;
LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
LL Pow(LL a,int b){
    LL ret=1;
    while(b){
        if(b&1)
           ret=ret*a;
        a=a*a;
        b>>=1;
    }
    return ret;
}
LL Polya(int n,int m){
    LL sum=0;
    //枚举n种旋转
    for(int i=1;i<=n;i++)
        //每个循环节是m种颜色可选
        //总共有gcd(n,i)个循环节
        sum+=Pow(m,gcd(n,i));
    if(n&1)
        //如果为奇数,所有位置上的循环节数量都为n/2+1
        sum+=n*Pow(m,n/2+1);
    else
        //否则要分奇偶,各一半
        sum+=n/2*Pow(m,n/2)+n/2*Pow(m,n/2+1);
    return sum/2/n;
}
int n,m;
int main(){
    while(scanf("%d%d",&m,&n)!=EOF&&n+m){
        printf("%lld\n",Polya(n,m));
    }
    return 0;
}

对于N如果非常大的话,枚举旋转就非常耗时。接下来可以有个优化

我们枚举循环节长度L,那么循环节个数便是N/L,必然L必须是N的约数才行。

那么d=N/L=gcd(i,N),对于每一个L,我们需要求出有多少个i满足左边的式子。

令i=d*t,gcd(d*t,L*d)=d,要左边的式子成立,明显gcd(t,L)==1,否则最大公约数不为d。

那么对于任意的t满足与L互质即可,便是L的欧拉函数值

这样就可以在sqrt(n)的复杂度内枚举L, ∑(phi(L) * M^(N/L) ) % p  (L即枚举值) 。

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 100005
#define inf 1<<29
#define MOD 2007
#define LL long long
using namespace std;
LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
LL Eular(LL n){
    LL ret=1;
    for(int i=2;i*i<=n;i++){
        if(n%i==0){
            n/=i;
            ret*=i-1;
            while(n%i==0){
                n/=i;
                ret*=i;
            }
        }
    }
    if(n>1) ret*=n-1;
    return ret;
}
LL Pow(LL a,LL b){
    LL ret=1;
    while(b){
        if(b&1)
           ret=ret*a;
        a=a*a;
        b>>=1;
    }
    return ret;
}
LL Polya(int n,int m){
    LL sum=0;
    int i;
    for(i=1;i*i<n;i++)
        if(n%i==0){
            sum+=Eular(i)*Pow(m,n/i);
            sum+=Eular(n/i)*Pow(m,i);
        }
    if(i*i==n)  sum+=Eular(i)*Pow(m,i);
    if(n&1)
        sum+=n*Pow(m,n/2+1);
    else
        sum+=n/2*Pow(m,n/2)+n/2*Pow(m,n/2+1);
    return sum/2/n;
}
int n,m;
int main(){
    while(scanf("%d%d",&m,&n)!=EOF&&n+m)
        printf("%lld\n",Polya(n,m));
    return 0;
}

Source Code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define MOD 1000000007
#define LL long long
using namespace std;
int prime[30]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,
    47,53,59,61,67,71,73,79,83,89,97,101},cnt=25;
LL n,m;
LL eular(LL n){
    LL sum = 1;
    for(int i = 2; i <= sqrt(1.0 + n); ++i)
        if(n % i==0){
            sum *= (i-1);
            n /= i;
            while(n % i == 0){
                sum *= i;
                n /= i;
            }
        }
    if(n > 1)
        sum *= (n-1);
    return sum % MOD;
}
LL Pow(LL a,LL b){
    LL ret=1;
    while(b){
        if(b&1)
            ret=(ret*a)%MOD;
        a=(a*a)%MOD;
        b>>=1;
    }
    return ret;
}
LL Polya(){
    LL sum=0,i;
    for(i=1;i*i<n;i++){
        if(n%i==0){
            sum=(sum+eular(i)*Pow(m,n/i))%MOD;
            sum=(sum+eular(n/i)*Pow(m,i))%MOD;
        }
    }
    if(i*i==n)
        sum=(sum+eular(i)*Pow(m,i))%MOD;
    if(n&1)
        sum=(sum+n*Pow(m,n/2+1))%MOD;
    else
        sum=(sum+n/2*(Pow(m,n/2)+Pow(m,n/2+1)))%MOD;
    return (sum*Pow(2*n,MOD-2))%MOD;
}
int main(){
    LL t,cas=0;
    scanf("%I64d",&t);
    while(t--){
        scanf("%I64d%I64d",&m,&n);
        printf("Case #%I64d: %I64d\n",++cas,Polya());
    }
    return 0;
}
时间: 2024-10-22 07:39:35

HDU 3923 Invoker 【裸Polya 定理】的相关文章

[ACM] hdu 3923 Invoker (Poyla计数,快速幂运算,扩展欧几里得或费马小定理)

Invoker Problem Description On of Vance's favourite hero is Invoker, Kael. As many people knows Kael can control the elements and combine them to invoke a powerful skill. Vance like Kael very much so he changes the map to make Kael more powerful. In

[ACM] hdu 3923 Invoker (Poyla计数,高速幂运算,扩展欧几里得或费马小定理)

Invoker Problem Description On of Vance's favourite hero is Invoker, Kael. As many people knows Kael can control the elements and combine them to invoke a powerful skill. Vance like Kael very much so he changes the map to make Kael more powerful. In

hdu 3547 (polya定理 + 小高精)

DIY CubeTime Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 207    Accepted Submission(s): 111 Problem Description Mr. D is interesting in combinatorial enumeration. Now he want to find out the numb

hdu 1817 Necklace of Beads(Polya定理)

Necklace of Beads Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 630    Accepted Submission(s): 232 Problem Description Beads of red, blue or green colors are connected together into a circular

hdu 5080 Colorful Toy(计算几何+polya定理)

题意:n个点m条边,给边染色,有c种颜色,求染色方案有几种(旋转重合的方案记作同一种): 思路:旋转染色方案数用polya定理解决,每个置换群计算一次: 由计算几何的方法控制旋转,由sin,cos的有理数性质得,分别计算旋转0,90,180,270的方案数: #pragma comment(linker,"/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<c

polya定理小结

polya的精髓就在与对循环节的寻找,其中常遇到的问题就是项链染色类问题. 当项链旋转时有n种置换,循环节的个数分别是gcd(n, i); 当项链翻转时有n种置换,其中当项链珠子数位奇数时,循环节的个数是n/2+1 当项链珠子数是偶数个时,当翻转线穿过珠子时,循环节个数为n/2+1,否则为n/2; 1.poj 1286: 题目大意:用三种颜色对珠子数不超过24的项链染色,问有多少种染色情况. 这道题是最基本的polya定理考察,只要带入公式即可 #include<iostream> #incl

Polya定理,Burnside引理(转)

设G是一个集合,*是G上的二元运算,如果(G,*)满足下面的条件: 封闭性:对于任何a,b∈G,有a*b∈G; 结合律:对任何a,b,c∈G有(a*b)*c=a*(b*c); 单位元:存在e∈G,使得对所有的a∈G,都有a*e=e*a=a; 逆元:对于每个元素a∈G,存在x∈G,使得a*x=x*a=e,这个时候记x为a-1,称为a的逆元,那么则称(G,*)为一个群. 例:G={0,1,2,3,4....n-1}那么它在mod n加法下是一个群. 群元素的个数有限,称为有限群,且其中元素的个数称为

【POJ 1286】Necklace of Beads(polya定理)

[POJ 1286]Necklace of Beads(polya定理) Necklace of Beads Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7550   Accepted: 3145 Description Beads of red, blue or green colors are connected together into a circular necklace of n beads ( n <

[BZOJ1004] [HNOI2008] Cards (Polya定理)

Description 小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绝色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.Sun发现这个问题有点难度,决