zoukankan      html  css  js  c++  java
  • bzoj1004

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

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

    再加上置换(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
  • 相关阅读:
    mongo 语法总结
    关系型数据库和非关系型数据库
    navicat for mongodb激活 工具和 激活流程
    理财的重要性
    【日常记录】【unity3d】 OnTriggerEnter 和 OnCollisionEnter (2D) 的区别
    【日常记录】【unity3d】 2D跳跃过快导致角色某帧陷入地面
    【日常记录】【unity3d】 获取手柄轴的输入
    【Java】java 中的泛型通配符——从“偷偷地”地改变集合元素说起
    【日常记录】用 vs2015 编译 love2d 引擎时出现 依赖项目luajit编译失败的解决办法
    【翻译&转载】shader的导数函数介绍
  • 原文地址:https://www.cnblogs.com/phile/p/4473186.html
Copyright © 2011-2022 走看看