等价类计数问题首先要构造出群

首先,给出的洗牌法就相当于置换,

再加上置换(1)(2)(3)……(n),可以构成一个包含m+1个置换的置换群;

这里要解释一下构成置换群的四个条件

  1. 封闭性 任意两个置换相乘所得的置换还在群内 题目中已经给定保证任意多次洗牌都可用这m种洗牌法中的一种代替

  2. 结合性 显然置换相乘本身就满足结合律

  3. 单位元 存在一个单位元e是的a*e=a成立,显然置换(1)(2)(3)……(n)就是这样一个单位元

  4. 逆元   任意一个置换a都存在一个置换b使得a*b=e 这是有题目给定条件对每种洗牌法,都存在一种洗牌法使得能回到原状态。

这样置换群就弄出来了,然后就是根据Burnside引理,找出每个置换不动点的个数

每个置换可以拆成多个不相交的循环的积,不动点就要求每个循环内元素颜色相同

由于颜色存在数量限制,不能用polya求,只能用dp求出

最后注意求平均数设计到除法取模,我们还要求m+1的乘法逆元

 1 var s,r:array[0..200] of longint;
 2     f:array[0..70,0..22,0..22] of longint;
 3     ans,x,y,i,j,n,m,a,b,c,p,t:longint;
 4     v:array[0..200] of boolean;
 5 
 6 procedure exgcd(a,b:longint;var x,y:longint);
 7   var xx,yy:longint;
 8   begin
 9     if b=0 then
10     begin
11       x:=1;
12       y:=0;
13     end
14     else begin
15       exgcd(b,a mod b,x,y);
16       xx:=x;
17       yy:=y;
18       x:=yy;
19       y:=xx-a div b*yy;
20     end;
21   end;
22 
23 function calc(n:longint):longint;  
24 //f[i,j,k]表示到第i个循环,红色用了j次,蓝色用了k次,由于每个循环内颜色相同,所以绿色用的次数可以根据i,j,k算出
25   var t,i,j,k:longint;
26   begin
27     f[0,0,0]:=1;
28     t:=0;
29     for i:=1 to n do
30     begin
31       t:=t+s[i];
32       for j:=0 to a do
33         for k:=0 to b do
34         begin
35           f[i,j,k]:=0;
36           x:=t-j-k;
37           if (x>c) then continue;
38           if x<0 then break;
39           if j>=s[i] then f[i,j,k]:=(f[i,j,k]+f[i-1,j-1,k]) mod p;  //要涂就一定要足够涂满循环内全部元素
40           if k>=s[i] then f[i,j,k]:=(f[i,j,k]+f[i-1,j,k-1]) mod p;
41           if x>=s[i] then f[i,j,k]:=(f[i,j,k]+f[i-1,j,k]) mod p;
42         end;
43     end;
44     exit(f[n,a,b]);
45   end;
46 
47 begin
48   readln(a,b,c,m,p);
49   n:=a+b+c;
50   for i:=1 to n do
51     s[i]:=1;
52   ans:=calc(n);
53   for i:=1 to m do
54   begin
55     for j:=1 to n do
56       read(r[j]);
57     fillchar(v,sizeof(v),false);
58     fillchar(s,sizeof(s),0);
59     t:=0;
60     for j:=1 to n do
61       if not v[j] then
62       begin
63         x:=j;
64         inc(t);
65         while not v[x] do
66         begin
67           v[x]:=true;
68           inc(s[t]);  //统计循环规模
69           x:=r[x];
70         end;
71       end;
72     ans:=(ans+calc(t)) mod p;  
73   end;
74   x:=0;
75   y:=0;
76   exgcd(m+1,p,x,y);
77   writeln(ans*(x+p) mod p);  //注意通过扩展欧几里得求出的乘法逆元可能是负数
78 end.
View Code

 

posted on 2014-07-26 17:58  acphile  阅读(179)  评论(0编辑  收藏  举报