bzoj 2142 国家集训队试题 礼物

问题转化成求C(N,M) mod P p为非素数,那么我们可以将P分解质因数,

也就是 π pi^ci的形式,因为这些pi^ci是互质的,所以我们可以用crt将他们合并

那么问题就转化成了快速求C(N,M) mod pi^ci

那么我们看下c的形式,为N!/(M!(N-M)!) mod pi^ci

因为mod的数不是质数,所以分母没法正常求逆元,那么我们可以将分子分母

中的pi的值挑出,那么我们先求N!,可以发现,N!mod pi^ci可以分段,每段是

pi^ci长,这一段的值是0--(pi^ci-1),那么因为我们需要将N!中pi|I的挑出来

剩下一些数,那就是好几段这个数相乘,用快速幂解决就行了,设cnt为p!其中

不包括pi|n的

那么N! mod pi^ci就变成了cnt^x*pi^y,那么x,y我们可以求出来,然后div掉p

之后,就又出现了一个阶乘,那么递归去做就行了。

比如N=11 pi=2 ci=2

N!为1*2*3*4*5*6*7*8*9*10*11

那么就是[1*3]*[5*7]*[9*11] mod pi^ci

挑出来的数是2,4,6,8,10,都div pi之后是

1,2,3,4,5 然后就成一个子问题了。

/**************************************************************

    Problem: 2142

    User: BLADEVIL

    Language: Pascal

    Result: Accepted

    Time:492 ms

    Memory:228 kb

****************************************************************/

 

//By BLADEVIL

var

    m, n                                :longint;

    pj, c                               :array[0..110] of longint;

    pi, s, a                            :array[0..110] of int64;

    p                                   :int64;

    tot                                 :longint;

  

procedure divide(p:int64);

var   

    i, j                                :longint;

      

begin

    tot:=0;

    for i:=2 to trunc(sqrt(p)) do

    if p mod i=0 then

    begin

        inc(tot);

        pj[tot]:=i;

        while p mod i=0 do

        begin

                inc(c[tot]);

                p:=p div i;

        end;

    end;

    if p>1 then

    begin

        inc(tot);

        pj[tot]:=p;

        c[tot]:=1;

    end;

    for i:=1 to tot do

    begin

        pi[i]:=1;

        for j:=1 to c[i] do pi[i]:=pi[i]*pj[i];

    end;

end;

  

function ex_gcd(a,b:int64;var x,y:int64):int64;

var   

    t                                   :int64;

begin

    if (b=0) then

    begin

        x:=1;y:=0;

        exit(a);

    end;

    ex_gcd:=ex_gcd(b,a mod b,x,y);

    t:=x;

    x:=y;

    y:=t-(a div b)*y;

end;

  

function gcd(a,p:int64):int64;

var   

    x, y                                :int64;

      

begin

    x:=0;y:=0;

    ex_gcd(a,p,x,y);

    x:=(x mod p+p)mod p;

    exit(x);

end;

  

function mi(x,y,q:int64):int64;

var

    rec                                 :int64;

      

begin

    rec:=1;

    while (y>0) do

    begin

        if y and 1=1 then rec:=rec*x mod q;

        x:=x*x mod q;

        y:=y shr 1;

    end;

    exit(rec);

end;

  

function fac(n,p,q:int64):int64;

var

    cnt                                 :int64;

    i                                   :longint;

begin

    cnt:=1;

    for i:=1 to n do

        if (i mod p>0) then

            cnt:=cnt*i mod q;

    exit(cnt);

end;

  

function fact(n:int64;var sum:int64;p,q:int64):int64;

var

    cnt, rec                            :int64;

      

begin

    rec:=1;

    cnt:=fac(q,p,q);

    while n>=p do

    begin

        sum:=sum+n div p;

        if (n div q>0) then rec:=rec*(mi(cnt,n div q,q) mod q)mod q;

        if (n mod q>0) then rec:=rec*(fac(n mod q,p,q)mod q) mod q;

        n:=n div p;

    end;

    if n>1 then rec:=rec*fac(n,p,q) mod q;

    exit(rec);

end;

  

function combine(n,m,p,q:int64):int64;

var   

    ans1, ans2, ans3, ans               :int64;

    a, b, c                             :int64;

      

begin

    a:=0;b:=0;c:=0;

    ans1:=fact(n,a,p,q);

    ans2:=fact(m,b,p,q);

    ans3:=fact(n-m,c,p,q);

    a:=a-(b+c);

    ans:=mi(p,a,q);

    ans:=ans*ans1 mod q;

    ans:=ans*gcd(ans2,q) mod q;

    ans:=ans*gcd(ans3,q) mod q;

    exit(ans);

end;

  

function doit(n,m:longint):int64;

var

    i                                   :longint;

    x, y, sum                           :int64;

          

begin

    sum:=0;

    for i:=1 to tot do

            a[i]:=combine(n,m,pj[i],pi[i]);

    for i:=1 to tot do

    begin

        x:=0;y:=0;

        ex_gcd(s[i],pi[i],x,y);

        x:=(x mod pi[i]+pi[i])mod pi[i];

        sum:=(sum+((x*s[i] mod p)*a[i])mod p)mod p;

    end;

    exit(sum mod p);

end;

  

procedure main;

var   

    i                                   :longint;

    w                                   :array[0..100] of longint;

    ans                                 :int64;

    sum                                 :int64;

      

begin

    readln(p);

    divide(p);

    for i:=1 to tot do s[i]:=p div pi[i];

    readln(n,m);

    sum:=0;

    for i:=1 to m do

    begin

        readln(w[i]);

        inc(sum,w[i]);

    end;

    if sum>n then

    begin

        writeln('Impossible');

        exit;

    end;

    ans:=1;

    for i:=1 to m do

    begin

        ans:=ans*doit(n,w[i]) mod p;

        n:=n-w[i];

    end;

    writeln(ans);

end;

  

begin

    main;

end.

 

你可能感兴趣的:(ZOJ)