POJ-2409-Let it Bead&&NYOJ-280-LK的项链
2015-08-20 21:35
661 查看
POJ-2409-Let it Bead&&NYOJ-280-LK的项链–pólya定理
pólya定理
设G是n个对象的一个置换群,用m种颜色填充这n个对象,当置换群所形成的等价类被视为相同填充时,不同填充方案共有:L=1|G|(mc(g1)+mc(g2)+...+mc(gs))
其中,G={g1,g2,...,gs},|G|是置换操作的个数s,c(gi)是置换gi的循环节数
pólya定理 的证明各位可上wiki上查看,在此我对置换和循环节及循环节数进行说明:
置换
n个元素1, 2, …, n之间的一个置换1,2,3...,na1,a2,...,an 表示1被1到n中的某个数a1取代,2被1到n中的某个数a2取代,直到n被1到n中的某个数an取代,且a1,a2,...,an互不相同例如本题中一个旋转置换”顺时针旋转1个珠子“对应如下置换
1,2,3,...,n−1,n2,3,4,...,n,1
循环及循环节数
设有序列1, 2, 3, 4, 置换操作为”顺时针旋转2个珠子“,则有如下置换公式:1,2,3,43,4,1,2 3,4,1,21,2,3,4
可以发现两次置换后回到初始状态,这称为一个循环。并且3和1,2和4总是相互替换,我们可以将此置换描述为:{1, 3}{2, 4},一对大括号被称为一个循环节,循环节的个数就称为循环节数。
再例如如下置换公式:
1,2,3,4,53,5,4,1,2
就可被描述为: {1, 3, 4}{2, 5}
于本题中,置换群是旋转以及翻转,n个对象对应于n个珠子,m种颜色对应于珠子的不同颜色,不同的填充方案对应于不同的珠子串法,所以本题pólya定理适用,我们需要做的就是通过分析置换操作的个数、种类以及对应的循环节数,再利用利用公式得出L。
旋转
对于一条有n个珠子的项链,(逆时针旋转可被顺时针代替,反之亦然。为方便叙述,后文旋转统一为顺时针旋转)共有“旋转 0, 1, 2, …,n-1个珠子”这n个置换。假设置换操作为“旋转i个珠子”,显然在此旋转下必须至少移动(lmc代表最小公倍数) lmc(i, n) 个珠子才会回到初始状态,而 lmc(i, n) = i * n / gcd(i, n),所以对于每个珠子在此置换下共需移动 n / gcd(i, n) 个 i,也就是 n / gcd(i, n) 次置换才能回到初始位置,所以每个循环节的内都有 n / gcd(i, n) 个元素(或者说是珠子),所以循环节数为 n / (n / gcd(i, n)) = gcd(i, n)个。即 设置换操作为“旋转i个珠子”,循环节数为 gcd(i, n)。
翻转
对于一条有n个珠子的项链,虽然翻转操作的形式受n的奇偶性影响,但是置换的个数都只有n个:
奇数情况下,翻转对称轴唯一的选择就是处在一个珠子上,这样才能使得对称轴两边均有n/2(整数除法)个珠子可以相互交换,从而实现翻转。 由于有n个珠子,所以对称轴就有n个选择,所以奇数时翻转有n个置换操作。假设置换操作为“选择珠子i为对称轴进行翻转”,则有 (n-1)/2 个“有2个元素的循环节”,加上 1 个“只有一个元素(即处于对称轴上的珠子)的循环节”,共有 (n+1)/2 个循环节,即 设置换操作为“选择珠子i为对称轴进行翻转”,循环节数为 (n+1)/2。
偶数情况下,翻转对称轴有两种形式,共n个置换操作:
–翻转对称轴上没有珠子,共有n/2个选择,对应n/2个置换操作。任意一个置换操作,有 n/2 个“有2个元素的循环节”,即循环节数为 n/2。
–翻转对称轴上有两个珠子,共有n/2个选择,对应n/2个置换操作。任意一个置换操作,有 (n-2)/2 个“有2个元素的循环节“,加上 2 个“只有一个元素(即处于对称轴上的珠子)的循环节”,共有 n/2 + 1 个循环节,即循环节数为 n/2 + 1。
所以本题共有 |G|=n+n=2n 个置换操作,旋转对应n个,翻转对应n个,每个置换对应的循环节数如上所述,代入公式即可得到L。
代码为poj2409。将输入m替换为3即是nyoj280的代码:
/* * poj. 2409 * date. 2015.8.20 * 16ms 132kB */ #include <iostream> #include <cstdio> using namespace std; long long qpow(int a, int n) { //由于库函数pow()返回值为double, 所以这里用快幂重写 long long result = 1, base = a; while (n) { if (n&1) result *= base; base *= base; n >>= 1; } return result; } int gcd(int a, int b) { //计算最大公约数 return b ? gcd(b, a%b) : a; } long long polya(int n, int m) { if (n == 0) return 0; long long sum = 1; for (int i = 0; i < n; ++i) //旋转 sum += qpow(m, gcd(i, n)); if (n&1) //翻转 sum += n * qpow(m, (n+1)/2); //奇数情况 else sum += n/2 * (qpow(m, n/2) + qpow(m, n/2 + 1)); //偶数情况 return sum/(n*2); } int main(){ int n, m; while (~scanf("%d %d", &m, &n) && n && m) printf("%d\n", polya(n, m)); return 0; }
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析
- C#获取关键字附近文字算法实例