bzoj 2313 分组 2012-01-15

http://www.zybbs.org/JudgeOnline/problem.php?id=2313

 

_______________________________________________

对于n要求方案数:

    对于n的约数s,即将n个数分为s组,每组有n/s=p个数共有:

      ans (p)=(C(p,n)*C(p,n-p)*C(p,n-2p)*...*C(p,p))/(s!)

                 =(n!*(n-p)!*...*p!)/((p!)^s*(n-p)!*(n-2p)!..1!*s!)

                 =(n!)/((p!)^s*s!)

种方案

    所以总共的方案数就有 total=sigma( ans(si))

    题目要求的是  m^total mod (10^9-401)

    设prime=10^9-401

    prime-1 分解质因数为 di[i]={2,13,5281,7283}

    首先需要知道:①费尔马小定理: a^(p-1)=1(mod p)

                            ②  (k-1)! =-1 (mod k)

    所以 m^(prime-1)=1 (1 mod prime);

            m^ans=m^(ans mod (prime-1)) (mod prime);

   所以现在需要求 ans mod (prime-1)

   等价于求满足 ans =xi (mod di[i])  (1<=i<=4)  最后将每一个yi 用中国剩余定理合并即求出 x=ans( mod (prime-1))。

   最后即可算出每一个x的和即为 total mod (prime-1)

   对于 ans=(n!)/((p!)^s*s!) mod di

   怎么求 n! mod di 呢?

   因为 n!=1*2..(di-1)*di*(di+1)*..*(q*di)*(q*di+1)...*n (mod di)

               =((di-1)! mod di)^q*(n-q*di)!*q!*(di)^q ( mod di)

               =(-1)^q*(n-q*di)!*q!*di^q ( mod di)

  因为(n-q*di)不是特别大(n-q*di)! mod di的值可以预处理出来

   所以可以用递归求一个数的阶乘模di的值   求出来的值可以化简为 fac*di^pow   

  将分数ans 的分子存成  di^pow1*fac1

                       分母存成  di^pow2*fac2

  易证pow1>=pow2 所以当 pow1>pow2时  ans= (fac1/fac2) (mod di);

                                      当 pow1=pow2时  ans= 0 (mod di)    

  !!!程序合并ans =xi (mod di[i])   用的是求线性模方程组的方法,具体百度

_______________________________________________

  1 Program Stone;
  2 const di:array[1..4]of longint=(2,13,5281,7283);
  3       prime=999999599;
  4 type data=record pow,fac:int64;end;
  5 var i,j,t:longint;
  6     n,m,ans:int64;
  7     aj:array[1..4,0..8000]of int64;
  8     s_up,s_low:array[1..4]of data;
  9     ci:array[1..4]of int64;
 10   function pow(c,x,p:int64):int64;  //求 c^x mod p 的值
 11   var i,k:int64;
 12    begin
 13      pow:=1;
 14      i:=x;
 15      k:=c;
 16      while i>0 do
 17       begin
 18         if i mod 2=1 then pow:=k*pow mod p;
 19         i:=i shr 1;
 20         k:=k*k mod p;
 21       end;
 22    end;
 23 
 24   function factorial(p,a:int64):data;  //求 p! mod di[a] 的值,存储成 fac*di[a]^pow
 25   var i,j:int64;
 26       s:data;
 27    begin
 28       i:=p div di[a];j:=p mod di[a];
 29       s.fac:=1;s.pow:=0;
 30       if i>0 then s:=factorial(i,a);
 31       if i mod 2=1 then s.fac:=-s.fac;
 32       s.fac:=(s.fac*aj[a,j]) mod di[a];
 33       s.pow:=(s.pow+i);
 34       factorial:=s;
 35    end;
 36   function exgcd(a,b:int64;var x,y:int64):int64;   //扩展欧几里德
 37 
 38   var t:int64;
 39    begin
 40      if b=0 then begin x:=1;y:=0;exit(a);end;
 41      exgcd:=exgcd(b,a mod b,x,y);
 42      t:=x;
 43      x:=y;
 44      y:=t-(a div b)*y;
 45    end;
 46   function work(a,b,n:int64):int64;   //求解  ax=n (mod b)的x 值
 47   var y,g:int64;
 48    begin
 49      g:=exgcd(a,b,work,y);
 50      work:=((work*(n div g) mod (b div g)+(b div g))mod (b div g));  //求出的解应该为最小整数解
 51    end;
 52   function lcm(a,b:int64):int64;   //求最小公倍数
 53   var i,j,k,gcd:int64;
 54    begin
 55      i:=a;j:=b;
 56      while j<>0 do
 57       begin
 58         k:=i;i:=j;j:=k mod j;
 59       end;
 60      lcm:=a*b div i;
 61    end;
 62   Procedure doans(i:int64);   //对于将n分成每组有i 个的方案数
 63   var j:longint;
 64       ai,yi,xi,k:int64;
 65       s:data;
 66    begin
 67      for j:=1 to 4 do    //s_up表示分子,s_low表示分母
 68         begin
 69          s_low[j]:=factorial(n div i,j);
 70          s:=factorial(i,j);
 71          s_low[j].pow:=s_low[j].pow+s.pow*(n div i);
 72          s_low[j].fac:=s_low[j].fac*pow(s.fac,n div i,di[j]);
 73          if s_up[j].pow<>s_low[j].pow then ci[j]:=0
 74                                       else ci[j]:=work(s_low[j].fac,di[j],s_up[j].fac);
 75         end;
 76         ai:=di[1];yi:=ci[1];    //合并ans =xi (mod di[i])   
 77         for j:=2 to 4 do
 78         begin
 79           xi:=work(ai,di[j],(ci[j]-yi));
 80           yi:=ai*xi+yi;
 81           ai:=lcm(ai,di[j]);
 82         end;
 83         ans:=(ans+yi)mod (prime-1);
 84    end;
 85   Procedure main;
 86   var i,j:longint;
 87       s:data;
 88    begin
 89       if n>1 then ans:=2 else ans:=1;
 90       for i:=1 to 4 do
 91         s_up[i]:=factorial(n,i);
 92       for i:=2 to round(sqrt(n)) do    //枚举n的约数
 93        if n mod i=0 then
 94          begin
 95            doans(i);
 96            if n div i<>i then doans(n div i);
 97          end;
 98    end;
 99 Begin
100  assign(input,'input.in');reset(input);
101   readln(t);
102   for i:=1 to 4 do
103    begin
104      aj[i,0]:=1;
105      for j:=1 to di[i] do
106        aj[i,j]:=aj[i,j-1]*j mod di[i];    //预处理 i! mod di[i]的值 (i<=di)
107    end;
108   for i:=1 to t do
109    begin
110      readln(n,m);
111      main;
112      writeln(pow(m,ans,prime));
113    end;
114 end.
115 
116  

 

posted on 2016-03-02 20:51  Yesphet  阅读(336)  评论(0编辑  收藏  举报