题目大意:长度为n的项链,要染m种颜色,可以通过旋转或翻转到达的状态视为同一种,问有多少种染色方案。
学了一波polya定理,发现很好理解啊,其实就是burnside定理的扩展。
burnside定理告诉我们不同染色方案数是每种置换的不变元素个数除以置换总数,而polya定理就是在这个基础上用公式计算出置换的不变元素个数。而且polya定理非常好理解,我们要让元素不变,所以对于每个循环节我们要染一样的颜色,有m种颜色,c(pk)个循环节,于是每种置换的不变元素个数就是m^c(pk)。
对于这道题,有两种操作。
①旋转。有n种置换,分别是1次旋转1个珠子,2个,3个...n个。对于1次旋转i个珠子,我们要求出循环节数可以先求出循环长度,一个位置置换x次之后回到原位,所以x=n*y/i,要使x尽量小且为整数那么n*y只能是lcm(n,i),于是循环长度为lcm(n,i)/i,那么循环节数为总长除以循环长度,n/(lcm(n,i)/i)=gcd(n,i)【n*i/gcd(n,i)=lcm(n,i)】。所以1次旋转i个珠子的循环节数为gcd(n,i)。
②翻转。对于奇偶性分类讨论。
(1)n为奇数。对于每个点作为对称轴左右翻转,则共n个置换,循环节数(n+1)/2。
(2)n为偶数。
a.对于对称的两个点作为对称轴左右翻转。n/2个置换,循环节数(n+2)/2。
b.对于两个点中间的空格作为对称轴左右反转,n/2个置换,循环节数n/2。
则共n个置换。
所以不论奇偶总置换数为2n,答案为sum/2n。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> using namespace std; int n,m; int qp(int a,int b) { int t=1,y=a; while(b) { if(b&1)t*=y; y*=y; b>>=1; } return t; } int gcd(int a,int b){return b?gcd(b,a%b):a;} int main() { while(~scanf("%d%d",&m,&n)&&(m||n)) { int sum=0; for(int i=1;i<=n;i++)sum+=qp(m,gcd(n,i)); if(n&1)for(int i=1;i<=n;i++)sum+=qp(m,(n+1)/2); else for(int i=1;i<=n/2;i++)sum+=qp(m,(n+2)/2)+qp(m,n/2); printf("%d\n",sum/(2*n)); } }
时间: 2024-10-10 13:09:25