置换群Polya定理

Polya定理通常用来解决置换群中的等价类计数问题;

设G = {a1,a2,a3,,,,,,am}是  N = {1,2,3,4,,,,n}上的一个置换群,现在用k中颜色对着n种对象进行染色,其不同的染色方案为:

ans = (k^c1 + k^c2 + k^c3 + ... k^cm)  / m(ci为在置换ai的运算下循环的总数)

求解过程中关键是求ci的值。

Polya定理简单模板题目poj2409;

题目的的难点在于找每个置换的循环个数,其中,题目有两种置换:

一种是手镯旋转:旋转的情况下会有n中置换(隔0,1,2,,,,,n-1旋转各是一种置换),其中隔i颗珠子的间距在旋转过程,它的每个循环构成的元素个数为n/gcd(n,i)(因为n与i的

最小公倍数为n*i/gcd(n,i),用它除以每次旋转的间隔i,则为达到最小公倍数时,所需的旋转次数,即为循环中元素个数。),所以它的循环次数为gcd(n,i)(因为它的每次循环的

元素个数相同)

另一种是翻转,翻转比较好理解,分奇、偶和对称轴是否在珠子上讨论就好。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<string>
#define ll long long
#define MAX 1000
#define eps 1e-8
#define INF INT_MAX

using namespace std;

ll gcd(ll a, ll b){
	if (b == 0) return a;
	gcd(b, a%b);
}

ll p[MAX];

int main(){
	int c,n;
	while (scanf("%d%d",&c,&n) && !(c == 0 && n == 0)){
		p[0] = 1;
		for (int i=1; i<=n; i++) p[i] = p[i-1]*c;
		ll ans = 0;
		for (int i=0; i<n; i++) ans += p[gcd(i,n)];
                if (n % 2){
			ans += n*p[(n+1)/2];
		}
		else{
			ans += n/2*(p[n/2] + p[n/2+1]);
		}
		ans = ans/(2*n);
		printf("%lld\n",ans);
	}
	return 0;
}

poj2154  polya定理,这个题对时间要求比较高,需要用欧拉定理优化,降低时间复杂度,再求欧拉函数的时候用到质因数分解,降低时间复杂度

#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#include<cstdlib>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<stack>
#define ll long long
#define MAX 36000
#define INF INT_MAX
#define eps 1e-8

using namespace std;

int P;

int mod_pow(int x, int n, int N)                
{  
    int ret = 1;  
    x %= N;  
    while (n)  
    {  
        if (n & 1)  
            ret = ret * x % N;  
        x = x * x % N;  
        n >>= 1;  
    }  
    return ret;  
}  

int prime[MAX],mark[MAX],cnt;  

void Prime(){
    memset(mark,0,sizeof(mark));  
    cnt = 0;  
    for (int i=2; i< MAX; i++){  
        if (!mark[i]) prime[cnt++] = i;  
        for (int j=0; j<cnt && prime[j]*i <= MAX; j++){  
            mark[i*prime[j]] = 1;  
            if (i % prime[j] == 0) break;  
        }  
    } 
}

int oula(int x)  
{  
    int res=x;  
    for(int i=0; i<cnt&&prime[i]*prime[i]<=x; i++)  
    {  
        if(x%prime[i]==0)  
        {  
            res=res/prime[i]*(prime[i]-1);  
            while(x%prime[i]==0)  
            {  
                x/=prime[i];  
            }  
        }  
    }  
    if(x>1) res=res/x*(x-1);  
    return res % P;                  //此处要注意mod P,不然会WA,估计是因为下面调用的时候可能会爆int类型 
}
  
int main(){
	int T,n;
	Prime();
	scanf("%d",&T);
	while (T--){
		scanf("%d%d",&n,&P);
		int i,ans = 0;
		for (i = 1; i*i < n; i++){       
			if (n % i == 0){
				int t1 = oula(i) * mod_pow(n,n/i-1,P) % P;
				int t2 = oula(n/i) * mod_pow(n,i-1,P) % P;
				ans = (ans + t1) % P;
				ans = (ans + t2) % P;
				//ans = (ans + mod_pow(n,i-1,P)*oula(n/i) + mod_pow(n,n/i-1,P)*oula(i)) % P;
			}
		}
		if (i * i == n){
			int t = oula(i) * mod_pow(n,i-1,P) % P;
			ans = (ans + t) % P;
			//ans = (ans + mod_pow(n,i-1,P)*oula(i)) % P;
		}
		printf("%d\n",ans % P);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值