polya定理再小结

转自:http://www.cppblog.com/sdfond/archive/2010/02/06/107403.aspx

话说ICPC的题目是越来越难,因为经典的算法大家都知道了,因此出题的方向只能是要么把模型隐藏的很深,要么就把一系列算法知识综合起来考察,这个时候分析问题的能力和灵活运用知识的能力就显得尤为重要。
  polya定理在很久以前的ICPC题目中就已经出现过,不过那个时候大家对于置换群都了解不多,因此polya定理算是很生僻的一个东西。然而人类总是飞速的进步,现在互联网上铺天盖地的题解使得polya定理走出深闺,逐渐被广大acmer所熟知。但是魔高一尺道高一丈,出题人也逐渐把polya定理的题出得越来越难做,越来越不好想,今天我就来总结一下这些颇有难度的polya定理题目(包括Burnside引理)。
  polya定理主要就是解决一类着色问题,或者说是同构计数问题。设染色方案数是n,置换群个数是p,置换群长度是s,那么利用Burnside引理,通过考察每个染色方案和每个置换群,可以在O(nsp)时间复杂度计算出答案。polya定理巧妙的利用同一循环内着色相同这个事实,避开了枚举着色方案,使得复杂度降到了O(sp)。但是利用polya定理的条件就是对于染色没有限制,如果不满足这个条件(比如对于不同循环节的染色限制,颜色数的限制等等),就没法直接使用polya定理。另一方面,同构计数无论如何也无法避开找置换群,因此很多题目也在这个上面做文章,把置换群弄得非常多,使得常规的枚举无法满足要求,必须寻求优化解法。
  这样讨论下来就可以把polya定理分成几个等级,最简单的就是找出置换群,染色就行了,这类代表题目有:
    HOJ 2084 The Colored Cubes
    HOJ 2647 Megaminx
    POJ 1286 Necklace of Beads
    POJ 2409 Let it Bead
    HDU 1812 Count the Tetris
  这些题目都是polya定理最基本的应用,当然以后估计再也见不到这样的题目了,因为太赤裸裸了。

  第二个等级的题目难度就略微提升了一些,比如加入颜色限制,这样就要用Burnside引理:
    TJU 2795 The Queen's New Necklaces
  这个题目就是对每种颜色的个数进行了限制,不过因为置换群很有规律,我用的是多重集排列来计数的。
    UVa 10601 Cubes 
  也是限制了颜色数,但是这个题里置换群比较没规律,我是直接搜的。
    UVa 11255 Necklace 
  和上面两个题目类似,我也是用排列组合直接数了,写起来有些麻烦,也许用dp更简单些。
    POJ 2154 Color 
  这个题目是一个里程碑,它标志着一类题目的优化方法。同样是项链旋转,因为置换群数量太多,利用数论的知识优化。

  第三个等级就比较变态了,几乎没有一看就能想出来的题目。这类题目的特点是,非常综合,用到了很多知识;有的题目难点甚至不在求解同构计数的部分。
    POJ 2888 Magic Bracelet 难度指数:★★★ 
  一道很不错的题目,这里加入连接限制同时还考察优化,优化方法同上。连接限制如何处理?注意到项链个数很少,因此可以建图,然后分别求出每种颜色连接n个珠子后回到自身的方案数,累加即可,这里可以用矩阵快速幂求解。
    TJU 3352 Birthday Toy   难度指数:★★★ 
  这个题目出得也很不错,同样是加入链接限制,不过这里的连接限制很有规律性,是相邻的珠子不可以染成同色,因此可以列出递推关系,最后化简成公式求解。此外由于还是项链旋转,用到了一样的优化方式(这个优化方式已经被考烂了)。
    HDU 2481 Toy      难度指数:★★★★ 
  08年成都现场赛题目,难点在于求解递推关系。这个题目比较新颖的地方在于不是染色了,而是同构图计数。首先由于图形的特殊性可以推出递推关系(不是很好想),然后利用矩阵求解。此外这个题目把同一循环节的缩成了一个珠子,利用这用方式来考虑问题,是一种新的思路。此外由于还是项链旋转,用到了一样的优化方式(凡是跟项链有关的就没有不考这个的了)。这个题目详尽的题解在这里:http://hi.baidu.com/spellbreaker/blog/item/1a7d9902ff6844e409fa93fb.html 
    SGU 282 Isomorphism  难度指数:★★★★★ 
  这个题目比较新颖,置换非常多,因此需要一些巧妙的方法优化。置换的个数达到了n!,但是可以通过搜索来枚举每个循环节的长度,把复杂度降到了求解n的分拆数方案数那么多。设n = L1 + L2 + ... + Lm,那么满足这个类型的置换个数是n!/ (L1 * L2 * ... * Lm * k1! * k2! * ... * kt!),其中t是不同的循环节长度的个数,ki是每种循环节长度,这个公式的大体思想就是:首先因为是置换,因此每个循环节内的数是固定的,把一个循环节内的数看成1个就变成了多重集的排列,但是每个循环节(Li)内,第一个数有(Li - 1)种选法(只要不是自己就行),第二个数有(Li - 2)种选法,依次类推,因此每个循环节还要乘以(Li - 1)!;之后因为对于两个同样长度的循环节,一旦选定了位置,其实不可以互换,因此要把多余的方案除掉,最后就是上述的公式。找出了置换的个数,接下来的问题就是,因为题目是对边染色,因此要把点置换映射成边置换。通过小范围的观察可以发现(具体证明不太会):一个长度为L的点循环节对应[L/2]个边循环节,两个长度分别是L1、L2的点循环节对应gcd(L1,L2)个边循环节。因为polya定理同一循环节内染色相同,因此不必关心对应后的边循环节长度,只要知道个数即可(这一点很巧妙),所以这样映射便可以求出结果。最后要注意的是题目说明了模一个素数,因此可以预处理出逆元来。
    SPOJ 422 Transposing is Even More Fun    难度指数:★★★★★ 
  这个题目需要一些观察力。把地址转置之后对应的写下来,会发现,一个长度为L循环节只需要移动L-1次(利用一个元素进行对换),这样假设有k个循环节,那么答案就是2 ^ (a + b) - k,关键问题是如何求k。循环节的个数其实就是相当于地址右移若干个b位后本质不同的地址个数,这样就划归到了polya定理的范畴。长度为a+b的地址一共可以右移(a + b) / (a, b)次(之后就出现循环了),因此这就是置换的个数。现在分别考虑每个置换下不同的地址个数,设g =gcd(a, b),那么可以看成一共有(a + b) / g个珠子,每个大小是2 ^ g,这样如果移动i下,那么对应的本质不同的地址个数是(2 ^ g) ^ gcd(i, (a+ b) / g)多个(类似于项链旋转),最后累加然后除以总置换数即可。
  然后的问题就是如何高效求解,由于数据组数非常多,利用欧拉函数以O(sqrt(a + b))的复杂度依然TLE。后来参照cyx的论文,实现了一个理论复杂度看似很高但是实际很快的方法。记f[i]表示满足gcd(i, (a + b) / g)是i的个数,先把总数分配给1,然后对(a + b) / g因式分解,用类似bfs的方法,扩展当前状态,如果从x扩展到了xp,那么就把x总数的1/p分给xp,注意不要重复扩展,利用一个单调队列让每个和数都唯一的被它的最小素因子扩展一次即可。这个方法复杂度难以估计但是很快出解。

你可能感兴趣的:(polya定理再小结)