uva 10601 Cubes(等价类计数,Burnside引理)
告诉你12根棒子的颜色,问一共能拼成多少个不同的正方体?
(通过改变空间位置变成相同的正方体,则视为同一种方案)
一看到这题感觉束手无策,即使你空间想象能力再好,恐怕也难以想出方案数。。
Burnside引理提供了一个良好的工具,下面就以此题为例详细介绍Burnside引理及其应用。
/*
等价类计算问题,有力武器是Burnside引理:
总方案数 = 置换的不动点数的平均值。
(若一种方案s经过置换F后,任与原方案等价,则称s为F的一个不动点。)
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int a[6] , b[6] ;
LL C[15][15];
void calc_combin()
{
C[0][0] = 1;
for(int i=1;i<15;i++){
C[i][0] = 1;
for(int j=1;j<=i;j++)
C[i][j] = C[i-1][j-1] + C[i-1][j];
}
}
LL fixpoint(int m) // 计算循环长度为m时的不动点个数
{ // 一种置换可以写成多个循环相乘的形式,只要每个循环内部颜色
//相同即为不动点,否则不是 不动点 。
int n = 0;
LL ans = 1;
for(int i=0;i<6;i++) {
if(b[i] % m) return 0;
// 循环内部必须颜色相同,故每种颜色的物品数量一定是m的整数倍。
b[i] /= m;
n += b[i];
}
/*
每个循环看成一个位置,颜色相同的m个木棍看成一个物品。
n个物品(总共6种,每种b[i]个)放在n个不同位置 ,有多少种方案?
*/
for(int i=0;i<6;i++){
ans *= C[n][b[i]] ; // 从剩余n个位置中选b[i]个放这b[i]个物品。
n -= b[i] ;
}
return ans;
}
#define COPY memcpy(b,a,sizeof(a))
LL solve(){
LL ans = 0;
// 先求不动点数的总和,总共有24中置换方法,下面依次列举。
//① 静止不动,循环长度为1
COPY;
ans += fixpoint(1);
//② 以两个相对的面的中心点的连线为轴(有3条这样的对称轴),
//沿一方向旋转90° 、 180° 、270°
COPY;
ans += 3 * fixpoint(4) ; // 90° ,循环长度为4
COPY;
ans += 3 * fixpoint(2) ; // 180° ,转两次回到原位,即循环长度为2.
COPY;
ans += 3 * fixpoint(4) ; // 270° 。
//③ 以两个相对的棱的中心点的连线为轴(有6条这样的对称轴),
//沿一方向旋转180° , 这两条棱静止不动,不参与循环。
for(int i=0;i<6;i++) // 枚举两条静止的棱的颜色.
for(int j=0;j<6;j++){
COPY;
if(--b[i]<0 || --b[j]<0) continue;
ans += 6 * fixpoint(2); // 循环长度为2
}
// ④ 以体对角线为轴(有4条) ,旋转120°、240° , 循环长度均为3.
COPY ;
ans += 8 * fixpoint(3); // 120° 和 240° 各四种
return ans / 24; // 每种置换的不动点数的平均值。
}
int main()
{
calc_combin();
int T;
scanf("%d",&T);
while(T--){
memset(a,0,sizeof(a));
int x;
for(int i=0;i<12;i++){
scanf("%d",&x);
a[x-1]++;
}
printf("%lld\n",solve());
}
return 0;
}