首先Burnside引理和Polya定理是什么关系呢?Polya定理是Burnside引理在涂色问题方面的一个应用。
Burnside引理讲的是在一个置换群中本质不同的方案数L和每k个置换之下不变元素的个数的关系,其可以表示成为一个公式:L=1/G*(D(ai)的和,i=1,2......k)。其中G为置换群的元素总数。
而Polya定理是针对给一个轴对称图形涂色问题的,对于这个图形,假设有m个颜色,置换数为N,每个置换的循环节数为c(i)其本质不同的方案数=1/N*(m^c(i)的和,其中i=1,2......N)。
由于关于这个Polya引理的相关资料以及相关题目的解释破碎不堪,所以我只好带着问题去看ac的代码推测哪些结论是可用的,以下是我提出的问题和相关解释:
1. 是不是旋转置换一定遵循循环节的个数一定是这个数的约数?//答案是肯定的,对于旋转变换,循环节的个数一定等于环所包含珠子总数n的约数d,而且,循环节为d的循环的个数=euler(n/d)。另一种变换叫做翻转变换,对于奇数个点的翻转变换,循环节为(n+1)/2,这样的循环节一共包含n个,而对于偶数个点的反转变换,循环节为(n/2+1)的有n/2个,而循环节为(n/2)的有n/2个--这个方案只适用于平面系统。
2. 是不是Polya只适用于颜色随便涂的题?如果不是怎么处理颜色有限制的题目?(例如:UVA的一道相关题目:cubes)//这个题目我在两份资料上看的对着循环节以及其中的分类方式不相同,但我认为对于这个Polya定理的理解,应该说从网络上看到的代码是对的,而书上对这个代码的解释是对的,这个问题就是对Polya定理的组合数解释,不可以直接用定理的原因很明确,是因为颜色不是随便用。但是仍然可以运用定理当中对于循环节染色的思想。这里就涉及到了一个非常重要的问题,就是正方体的旋转循环节怎么找。这里我更偏向于书上的解释为轮廓,网上的解释为中心,因为这样解释恰好能解释网上的ac代码。二者总体思路是一样的:立方体有这样几个可用的旋转轴(1)对顶点--可旋转角度为120°240°两组,一共有四组这样的对顶点,对应的循环节的长度为3--对应的代码4*2*slove(3)。(2)两个对立面的中点,一共有三组这样的轴线,对应的旋转度数为90°180°270°,分别对应循环节长度为4,2,4--对应的代码,循环节为4的:3*2*slove(4),循环节为2的3*slove(2)。(3)两个对立棱为中心,一共六组,每组的循环节为2--对应的代码:6*slove(2)。(4)不动,对应的循环节长度为1,一共一组--对应的代码slove(1)。这样所有的循环节就找清楚了,然后就是公式的改造与应用,由于可以用的颜色受到限制,所以这个题目把公式中的幂次方改造,思路还是一样的,都是把循环节着色为同一色,这样就用到了组合计数。当然,若不能正好处理成为每个循环节同色,这个着色方式就是不可行的。
ac代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
long long a[7],b[7];
long long c[15][15];
long long solve(long long k){
long long sum=1;
long long n=0;
long long i,j;
for (i=1;i<=6;i++){
if (b[i]%k==0){
b[i]/=k;
n+=b[i];
}
else return 0;
}
for (i=1;i<=6;i++){
sum*=c[n][b[i]];
n-=b[i];
}
return sum;
}
long long polya(){
long long i,j;
long long ans=0;
memcpy(b,a,sizeof(a));
ans+=solve(1);
memcpy(b,a,sizeof(a));
ans+=8*solve(3);
memcpy(b,a,sizeof(a));
ans+=6*solve(4);
memcpy(b,a,sizeof(a));
ans+=3*solve(2);
for(i=1;i<=6;i++){
for(j=1;j<=6;j++){
memcpy(b,a,sizeof(a));
b[i]--;b[j]--;
if(b[i]<0||b[j]<0) continue;
ans+=6*solve(2);
}
}
return ans/24;
}
void getc(){
long long i,j;
for (i=0;i<=12;i++){
c[i][0]=c[i][i]=1;
for (j=1;j<i;j++){
c[i][j]=c[i-1][j-1]+c[i-1][j];
}
}
}
int main(){
getc();
long long i,j,t,x;
scanf("%lld",&t);
while (t--){
memset (a,0,sizeof(a));
for (i=1;i<=12;i++){
scanf("%lld",&x);
a[x]++;
}
printf("%lld\n",polya());
}
}