http://poj.org/problem?id=1286
题意:有红、绿、蓝三种颜色的n个珠子,要把它们构成一个项链,问有多少种不同的方法。旋转和翻转后相同的属于同一种方法。
polya计数。
搜了一篇论文Pólya原理及其应用看了看polya到底是什么东东,它主要计算全部互异的组合的个数。对置换群还是似懂略懂。用polya定理解决问题的关键是找出置换群的个数及哪些置换群,每种置换的循环节数。像这种不同颜色的珠子构成项链的问题可以把N个珠子看成正N边形。
Polya定理:(1)设G是p个对象的一个置换群,用k种颜色给这p个对象,若一种染色方案在群G的作用下变为另一种方案,则这两个方案当作是同一种方案,这样的不同染色方案数为:;
(2) 对于N个珠子的项链,共有n种旋转置换和n种翻转置换。
对于旋转置换:每种置换的循环节数c(fi) = gcd(n,i),(i为一次转过多少个珠子)
对于翻转置换:如果n为奇数,共有n种翻转置换,每种置换的循环节数c(f) = n/2 + 1;
如果n为偶数,c(f) = n/2的置换有n/2个; c(f) = n/2 + 1的置换有n/2个。
直接带入公式就KO了。
poj 1286
#include <stdio.h> #include <iostream> #include <algorithm> #include <set> #include <map> #include <vector> #include <math.h> #include <string.h> #include <queue> #include <string> #include <stdlib.h> #define LL long long #define _LL __int64 #define eps 1e-8 using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 10; int n; _LL ans; int gcd(int a, int b) { if(b == 0) return a; return gcd(b,a%b); } int main() { while(~scanf("%d",&n)) { if(n == -1) break; if(n == 0) //不考虑n=0的情况,会导致RE { printf("0\n"); continue; } ans = 0; // n 种旋转置换 for(int i = 1; i <= n; i++) ans += pow(3.0,gcd(n,i)); //m种翻转置换 if(n & 1) { ans += n * pow(3.0,n/2+1); } else { ans += n/2 * pow(3.0,n/2); ans += n/2 * pow(3.0,n/2+1); } ans = ans/2/n; printf("%I64d\n",ans); } return 0; }
poj 2409
#include <stdio.h> #include <iostream> #include <algorithm> #include <set> #include <map> #include <vector> #include <math.h> #include <string.h> #include <queue> #include <string> #include <stdlib.h> #define LL long long #define _LL __int64 #define eps 1e-8 using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 10; int c,s; _LL ans; int gcd(int a, int b) { if(b == 0) return a; return gcd(b,a%b); } int main() { while(~scanf("%d %d",&c,&s)) { if(c == 0 && s == 0) break; ans = 0; for(int i = 1; i <= s; i++) ans += pow(c*1.0,gcd(s,i)); if(s & 1) { ans += s * pow(c*1.0,s/2+1); } else { ans += s/2 * pow(c*1.0,s/2); ans += s/2 * pow(c*1.0,s/2+1); } ans = ans/2/s; printf("%I64d\n",ans); } return 0; }