Burnside引理:记C(f)为在置换f下保持不变的着色方案的个数,那么本质不同的着色方案数位所有置换f的C(f)值的平均数。
如果求给定置换C(f)的方法为“一次判断每个着色方案是否在该置换下不变”由于每个方案包含p个“格子”的颜色信息,考察每个方案的复杂度为O(p),考察所有n种着色方案的复杂度为O(np),由于有s种置换方案,因此时间复杂度为O(nsp).
Polya定理:如果用k种颜色给有限集s着色,设m(f)为置换f的循环节的个数,则C(f)=k^m(f)(因为每个循环节之间的元素颜色应相同)
带入Burnside引理得到Polya定理。时间复杂度O(ps).
poj 1286 Necklace of Beads
题意:用三种颜色的珠子串成一个长度为N(N<24)的项链,项链可以旋转和翻转,经过旋转和翻转所得的项链视为同一种项链,现在告诉你项链的长度s,求共能组成几条不同的项链。
结论:对于旋转置换群,群内置换的总个数显而易见是n个,第i个置换中循环节个数应该是gcd(n,i)个,且第i个元素于i+n/gcd*j个元素在同一循环节中;
或者使用欧拉函数优化,只处理n的因子i,ans+=Math.pow(s,i)*euler(n/i);(i|n,注意处理i*i=n时)
对于翻转置换,上面的那个图给了很大提示,找循环节的关键是找对称轴。
当n为奇数,那么对称轴就是每个点和圆心的连线,共n条,那么显然除了这个点没变,其他的点都跟对称的那个点置换了,所以循环节的个数是(n-1)/2+1。
当n为偶数,那么对称轴有每个点和对面的点的连线,共n/2条,显然除了对称轴上的两个点,其余点都跟对面的点置换了循环节的个数是(n-2)/2+2,两个相邻点中点和圆心的连线也是n/2条,显然每个点都跟对面的点置换了,循环节的个数是n/2。
poj 2514 Color
解:利用颜色数等于珠子数且仅考虑旋转置换的特殊性进行mod运算,由于每个因子都是n的幂形式组成,因此颜色数s可看做n%p,每次进行i次幂运算时做i-1次,抵消最后需要除的n;n=s=10^8,欧拉函数和快速幂优化.
有限制条件的Polya问题:Poj2888 Magic Bracelet
题意:只考虑中心对称,但是某些颜色的珠子不能相邻。
解法:根据Polya定理,sum=Sigma{Euler(n/i)*get(i)};(n%i==0)其中get(i)是为i个循环节染色的方案数,不做限制时get(i)=s^i;但这里get(i)是要满足所有限制条件的染色方案,所以我们把m种颜色的关系画成一个无向图,而get(i)就是长度为 i 的回路的个数。即对邻接矩阵做i次幂后对角线元素的和。
Hdu 2865 Birthday Toy
限制条件:相邻的珠子颜色不能相同。
解法:如果颜色数少,可构造对角线为0其余元素为1的颜色矩阵,由于颜色数巨大,因此求get(i)时使用递推式,假设固定第一个珠子,设f(n)为最后一个珠子与第一个珠子颜色不相同的方案数,g(n)为最后一个珠子与第一个珠子颜色不相同的方案,则f(n)=f(n-1)*(s-2)+g(n-1)*(s-1),g(n)=f(n-1) 即f(n)=(s-2)*f(n-1)+(s-1)*f(n-2), f(n)=(s-1)^n+(s-1)*(-1)^n.再利用乘法逆元解决mod问题即可。
import java.util.Scanner; public class BirthdayToy2865 { long mod = 1000000007; long euler(int x) { long i, res = x; for (i = 2; i * i <= x; i++) if (x % i == 0) { res = res / i * (i - 1); while (x % i == 0) x /= i; // 保证i一定是素数 } if (x > 1) res = res / x * (x - 1); return res; } int cc, n;//颜色数 珠子数 long get(long n) { long ans = 0; ans = pow(cc - 1, n); if ((n&1)==0) ans = (ans + cc - 1) % mod; else ans = (ans + mod - (cc - 1)) % mod; return ans; } long pow(long x, long p) { long ans = 1; while (p > 0) { if ((p & 1) == 1) ans = (ans * x) % mod; p >>= 1; x = (x * x) % mod; } return ans % mod; } long inv(int k) {//k在摸mod意义下的乘法逆元 return pow(k, mod - 2) % mod; } long polya() { long ans = 0; int i; for (i = 1; i * i <= n; i++) if(n%i==0){ ans = (ans + euler(i) * get(n / i)) % mod; if (i * i != n) ans = (ans + get( i) * euler(n / i)) % mod; } return (ans * inv(n)) % mod; } void run() { Scanner scan = new Scanner(System.in); while (scan.hasNext()) { n = scan.nextInt(); cc = scan.nextInt() - 1; System.out.println((polya() * (cc + 1)) % mod); } } public static void main(String[] args) { new BirthdayToy2865().run(); } }