polya计数定理

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,求共能组成几条不同的项链。

polya计数定理_第1张图片0-[

结论:对于旋转置换群,群内置换的总个数显而易见是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();
	}
}









你可能感兴趣的:(c)