群的提出是为了解决对称性问题。
置换可以简单理解为这几个位置的数来回换,一个置换可以写成若干循环的乘积,一个循环中元素的个数为M,则称其为m阶循环
置换->循环->对换
从下标为1的开始,理解成以[1]中数字为下标的元素值放到1中。 eg: a[1]=3 a[3]=2 ->a[1]=2 a[1]=a[[1]];
找一个群中的循环节,有两种方法一种是根据该元素对应的下标到那个中去找,直到有访问过的。另一种则是每次到这个下标,找到这个下标本应的元素在原序列中的位置
依次循环下去(cow sorting)
波利亚定理:
/*
PKU 2409
问题:给你m种不同颜色的珠子,你能穿出多少长度为n的“本质不同”首饰来。这里的“本质不同”指首饰经过翻转或旋转,不会与其他方案相同。我们称之为着色方案。例如图中给出的2种颜色,穿出5个珠子的首饰的各种方案示意图。
思路:
置换:【1,2,…,n | a1, a2,…an】 表示1被a1取代(a1为1到n的某数),2被a2取代……, a1,a2…,an不相同
置换群:置换群的元素是置换,运算时置换的连接。例如:【1,2,3,4 | 3,1,2,4】【1,2,3,4 | 4,3,2,1】 = 【1,2,3,4 | 2,4,3,1】
轮换:记(a1a2…an) = 【a1,a2,a3,…an | a2,a3,…,an,a1】
方案与方案之间其实是可以看做一种置换,即由其中一种方案通过置换可以变成另一种方案。例如有4个珠子,2种颜色,我们看一下前两个珠子白色,后两个黑色的情况。这种方案可以通过置换【2,3,4,1】可以变成第一个和第四个是黑色,第二个和第三个为白色的方案;
Burnside引理:
置换会使一个着色方案变成另一个方案。但是有些方案比较特殊,他在某些置换下会置换到他自身。我们称之在置换f下不变。记C(f)为置换f下保持不变的着色方案个数。那么就有一个结论:本质不同的着色方案数为所有置换f的C(f)值的平均数。
我们设f的置换群为G,则Burnside引理:等价类数目 N = sum{C(f)} / |G|, f属于G。
这个公式虽然简单,但是对于C(f)的求解还是个问题,于是介绍下面的定理。
Polya定理:
设G是N个对象上的置换群,用M种颜色涂染这N个对象,则不同的染色方案数为:
LG =( Mλ(g1)+Mλ(g2)+…+Mλ(gp))/|G|
其中G=(g1,g2, …gp), λ(gk)为置换gk的轮换的个数。
利用置换论中轮换的概念(轮换概念=循环概念),我们将置换f分解为若干循环。记f的循环节数位m(f)。
则有定理:C(f) = k ^ m(f) ,其中k为着色数。
于是有Polya定理:不同的着色方案数 l = sum(k ^ m(f)) / |G| ,其中f属于G。
对于本题还有如下结论:
在此种模型下,所有的置换可以通过一个置换旋转和翻转全部得来。
(1)旋转
将置换顺时针旋转i格,其循环节数的为gcd(n, i);
(2)翻转
当n为奇数:共有n个循环节数为(n+1)/2的循环群
当 n为偶数:共有n/2个循环节数(n+2)/2的循环群,和n/2个循环节数n/2的循环群。
Polya定理是个非常实用的定理,记p为格子数,s为着色数,则它可以在O(ps)内结果着色问题。
*/
#include <iostream>
#include <math.h>
using namespace std;
typedef long long int64;
int64 gcd(int64 a, int64 b)
{
if (b) return gcd(b, a % b);
else return a;
}
int main()
{
int64 n, i;
double m;
while(cin>>m>>n, n || m)//n=length ,m is the number of colors
{
int64 ans = 0;
for (i = 1; i <= n; i++) ans += (int64)pow( m, (int)gcd(n, i) ); //m^gcd(n,i)
if (n & 1) ans += (int64)(pow(m, (int)(n + 1) / 2) * n);
else
{
ans += (int64)(pow(m, (int)n / 2) * (n / 2));
ans += (int64)(pow(m, (int)(n + 2) / 2) * (n / 2));
}
ans /= (2 * n);
cout<<ans<<endl;
}
return 0;
}