等价类计数问题首先要构造出群
首先,给出的洗牌法就相当于置换,
再加上置换(1)(2)(3)……(n),可以构成一个包含m+1个置换的置换群;
这里要解释一下构成置换群的四个条件
-
封闭性 任意两个置换相乘所得的置换还在群内 题目中已经给定保证任意多次洗牌都可用这m种洗牌法中的一种代替
-
结合性 显然置换相乘本身就满足结合律
-
单位元 存在一个单位元e是的a*e=a成立,显然置换(1)(2)(3)……(n)就是这样一个单位元
-
逆元 任意一个置换a都存在一个置换b使得a*b=e 这是有题目给定条件对每种洗牌法,都存在一种洗牌法使得能回到原状态。
这样置换群就弄出来了,然后就是根据Burnside引理,找出每个置换不动点的个数
每个置换可以拆成多个不相交的循环的积,不动点就要求每个循环内元素颜色相同
由于颜色存在数量限制,不能用polya求,只能用dp求出
最后注意求平均数设计到除法取模,我们还要求m+1的乘法逆元
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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.