为什么要分开呢?因为我觉得这块比较难理解,放在一起会“劝退”的
!!!一定要搞清楚上一部分的各种定义和符号
Burnside引理:
置换群G作用下等价类个数,等于每个置换 的不动点个数的平均数。
当然不动点类其实是有一个公式的( 表示元素 的不动置换类, 表示置换 的不动点集)
因为其实是两个两两对应的东西,所以应该很好理解
所以Burnside引理得出:
等价类个数
举个例子: 经典的着色,有2*2的格子,每个1*1可以选择涂或不涂,且格子可以顺时针旋转,求方案数 着是所有可能的情况,将每种情况看成元素集X中的元素,而顺时针旋转 作为置换群G中的元素 : : : : 那么 |
从这个例子可以看出,等价类内的元素属于重复情况,Burnside引理可以在存在置换情况时,计算出实际的方案数。
Polya定理:
为置换 的循环节(未涂色,单纯以元素编号为对象)个数,polya定理用 代替了 。
定理内容:用m种颜色对置换群G(操作对象群X)涂色,等价类的公式公式如下:
Polya定理相当于用一个较为容易得到的东西 , 替换了Burnside引理所需的不定点的个数,因为求循环节不需要列出所有的情况,只需要统计每种置换就可以得出答案
举个例子: 还是这个问题 现在要求的不是每个置换的不动点数 , 而是每个置换的循环数。 注意polya定理的G的元是单个元素(相较于Burnside引理,其G中元为涂色结果的整体,也就是说Burnside引理是上面16种情况而polya定理是4个独立方块) : : : : 那么 |
有没有感觉比上面枚举要简单很多呢
重点强调:
–Polya定理中的群G是作用在n个对象上的置换群
–Burnside引理中的群G是对这n个对象染色后的方案集合上的置换群
再写一遍,polya定理的G中元是单个元素,而Burnside引理的G中元为涂色结果的整体,也就是说Burnside引理是上面16种情况而polya定理是4个独立方块)。
例题:hdu 4633
有一个类似魔方的正方体,每面9个格子可涂不同颜色,而8个点和12条边也都涂色,正方体可以任意旋转,现有颜色K种,求方案数(两种正方体同构属于一种方案)
接下来根据polya定理,逐个分析每个置换的循环节数量
话说这部分如果空间想象力不好真的会比较难,你可以通过画图来帮助寻找哪些块在循环交换
- 不动:点8+面54+边12=74
- 按面旋转:(3对)
- 90:点2+面15(上下左右四面对应->9+前后(1379)(2468)(5)->3*2)+边3=20
- 180:点4+面28(上下对应->9+左右对应->9+前后(19)(28)(37)(46)(5)->5*2)+边6=38
- 270:同90=20
- 按边旋转:(6对)
- 180:点4+面27(上下对应->9+左右对应->9+前后对应->9)+边7=38
- 按顶点旋转:(4对)
- 120:点4+面18(正左下对应->9+后右上对应->9)+边4=26
- 270:点4+面18(正左下对应->9+后右上对应->9)+边4=26
所以最后的答案为:
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod=10007;
LL sw(LL a,LL b){
LL ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;b>>=1;
}return ans;
}
LL inv(LL a){return sw(a,mod-2);}
int main(){
int t;scanf("%d",&t);int ca=0;
while(t--){
LL K;scanf("%lld",&K);
printf("Case %d: %lld\n",++ca,(sw(K,74)+sw(K,20)*6+sw(K,38)*3+sw(K,38)*6+sw(K,26)*4*2)%mod*inv(24ll)%mod);
}
}
例题:hdu1812
n*n涂K色,同构:旋转翻转,求方案数
这题就比较水了,不翻转时有:
- 旋转0:n*n
- 旋转90或270:n奇时 ,n偶时 ,因为n偶时 ,所以可以直接用
- 旋转180:n奇时 ,n偶时 ,同理,直接用
翻转后有:
- 偶数的左右翻转和上下翻转为: ;
- 其他(偶数对角线,奇数上下左右对角线)为:
写成c语言的话,答案就是:
#define a^3 a*a*a
ans =( k ^ (n*n) )
+( k ^ ((n*n+3)/4) )*2
+( k ^ ((n*n+1)/2) )
+( k ^ ((n+1)*n/2) )*2
+( (n%2) ? ( k ^ ((n+1)*n/2) ) : ( k ^ (n*n/2) ) )*2
jave代码:
import java.util.*;
import java.math.*;
public class Main{
public static void main(String argv[]){
Scanner cin=new Scanner(System.in);
while(cin.hasNext()){
int n=cin.nextInt();
BigInteger k=cin.nextBigInteger();
BigInteger ans=BigInteger.valueOf(0);
ans=ans.add( k.pow(n*n) );
ans=ans.add( k.pow((n*n+3)/4).multiply(BigInteger.valueOf(2)) );
ans=ans.add( k.pow((n*n+1)/2) );
ans=ans.add( k.pow((n+1)*n/2).multiply(BigInteger.valueOf(2)) );
if(n%2==1)
ans=ans.add( k.pow((n+1)*n/2).multiply(BigInteger.valueOf(2)) );
else
ans=ans.add( k.pow(n*n/2).multiply(BigInteger.valueOf(2)) );
ans=ans.divide(BigInteger.valueOf(8));
System.out.println(ans);
}
}
}