/* 组合数学?? 查了很多资料这个题目很是很模糊。。 */ #include <iostream> #include <cstdio> using namespace std; long long Gcd(long long a, long long b){ if(b == 0) return a; return Gcd(b, a % b); } inline long long Pow(long long a, long long b){ long long ans = 1; for(int i = 1; i <= b; i++) ans *= a; return ans; } inline void Polay(int c, int n){ long long sum = 0; for(int i = 1; i <= n; i++) sum += Pow(c, Gcd(n, i)); long long temp; if(n & 1) temp = n * Pow(c, ((n >> 1) + 1)); else temp = (n >> 1) * Pow(c, n >> 1) + (n >> 1) * Pow(c, ((n >> 1) + 1)); sum = (sum + temp) / (n << 1); printf("%lld/n", sum); } int main(){ int c, n; while(scanf("%d %d", &c, &n) != EOF && c + n){ Polay(c, n); } } http://blog.csdn.net/shiren_Bod/archive/2010/06/11/5664934.aspx 要求知识:polya或者burnside 题意转述:一串由n个珠子组成的项链,用c种颜色涂染,问能形成多少种不同项链。 限制:旋转得来的为同一种,翻转得来的也为同一种。 本题有多种算法,下面给出比较容易想到且易于理解的两种算法。 推荐使用算法2,因为若是碰到高精度问题,算法2比算法1易于处理,且复杂度也比算法1低好多。核心知识点都是Polya定理。 解决过程: 算法一: I.计算旋转置换和翻转置换的个数: 1.旋转置换 N个珠子,把项链当成一个圆,每次旋转角度为I*360/N(I∈(1,n)),共有N次不同旋转。 2.翻转置换 ⊙考虑对称轴: n为奇数. 只有一种对称轴, 即轴穿过一个点. 这样有N种置换。 n为偶数, 有两种翻转: 轴每边n/2个点. 这样的置换有n/2个; 轴穿两点, 每边n/2个点. 这样的置换也有n/2个; 这样算出来结果还是有n个置换,即n为奇偶数是置换个数是一样的。 综上所述,共有2*N次不同置换,即有|G|=2N。 II.每个置换的循环节数 1.旋转置换 第i次旋转对应的置换结果: view plaincopy to clipboardprint? for(j=0; j<n; j++) { g[j]=(i+j)%n; } 循环节数count由一下循环求出: for(j=0, count=0; j<n; j++) { //求每个旋转置换的循环节数,和寻找连通子图类似. if(flag[j]==0) { //若j结点未曾访问过,表示一个新的循环,count++. count++; for(k=j; flag[k]==0; k=g[k]) flag[k]=1; } } 2.翻转置换 第i次翻转对应的置换结果: view plaincopy to clipboardprint? for(j=1; (j+j)<n; j++) { //每次翻转置换的结果,沿翻转轴对称点交换位置. //由于程序里每次旋转置换在翻转置换之前进行,故每次只要用 //同一个翻转循环处理,而不必分情况讨论. k=g[j]; g[j]=g[n-j]; g[n-j]=k; } 循环节数count由以下循环求出: for(j=0, count=0; j<n; j++) { //求每个翻转置换的循环节数,和寻找连通子图类似. if(flag[j]==0) { //若j结点未曾访问过,表示一个新的循环,count++. count++; for(k=j; flag[k]==0; k=g[k]) flag[k]=1; } } 算法二: 本题还可以快速算出每个置换的循环(或者轮换)个数,达到快速求解的目的: 分析(1) 1.旋转. 考虑顺时针旋转i格的置换: 循环个数为gcd(n,i) 每个循环的长度为L=n/gcd(n,i) 2.翻转 考虑对称轴 ***n为奇数. 只有一种对称轴, 即轴穿过一个点. 有[n/2]个循环长度为2, 还有一个循环长度为1(被穿过的点), V=C([n/2], [m/2]). ***n为偶数, 有两种翻转 轴每边n/2个点. 这样的置换有n/2个 轴穿两点, 每边n/2个点. 这样的置换也有n/2个 算法复杂度分析:很容易可以算出这个算法的时间复杂度为O(n)。 分析(2): Polya定理:设G是n个对象的一个置换群,用m种颜色涂染这n个对象,则不同染色的方案数L=1/|G|*[m^p(a1)+m^p(a2)+....+m^p(an)].其中p(ai)是某个置换的循环数. 1.旋转置换. 我们假设依次顺时针旋转1~n个,则循环个数=gcd(i,n); 2.翻转置换 当n为偶数时,分两种情况,一种是中心轴在两个对称对象上,则循环个数为n/2+1,另一种是对称轴两边分别有n/2个对象,则循环个数为n/2; 当n为奇数时,对称轴就只能在一个对象上,则循环个数为n/2+1; view plaincopy to clipboardprint? void polya(int p, int n) { int i; ans = 0; for(i=0; i<n; i++) //第i次旋转的循环节数为gcd(n,i); ans += pow(p*1.0, gcd(n, i)); if(n % 2) //n为奇数 { for(i=0; i<n; i++) //共有n个循环节数均为 n/2+1 的置换; ans += pow(p*1.0, n/2+1); } else //n为偶数 { for(i=0; i<n/2; i++) //如上面所述有两种置换,第一种循环节数均为n/2, ans += pow(p*1.0, n/2) + pow(p*1.0, n/2+1); //第二种循环节数均为n/2+1; } }