• 「一本通 6.6 练习 8」礼物


    Description

    原题来自:BZOJ 2142

    一年一度的圣诞节快要来到了。每年的圣诞节小 E 都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小 E 心目中的重要性不同,在小 E 心中分量越重的人,收到的礼物会越多。

    小 E 从商店中购买了 n 件礼物,打算送给 m 个人,其中送给第 iii 个人礼物数量为 wii​​。请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,你只需要输出模 PPP 后的结果。

    Input

    输入的第一行包含一个正整数 P,表示模数;

    第二行包含两个正整数 nm,分别表示小 E 从商店购买的礼物数和接受礼物的人数;

    以下 mmm 行每行仅包含一个正整数 wii​​,表示小 E 要送给第 iii 个人的礼物数量。

    Output

    若不存在可行方案,则输出 Impossible,否则输出一个整数,表示模 P 后的方案数

    Sample Input

    100
    4 2
    1
    2

    Sample Output

    12

    Sample Explaination

    12 种方案详情如下: {1}{2,3},{1}{2,4},{1}{3,4},{2}{1,3},{2}{1,4},{2}{3,4},{3}{1,2},{3}{1,4},{3}{2,4},{4}{1,2},{4}{1,3},{4}{2,3}

     Hint

    P=p1c1×p2c2×p3c3×⋯×ptci pi为质数1c1​​​​×p2c2​​​​×p3c3​​​​××ptct​​​​,pip_ipi​​ 为质数。

    对于 100%的数据,1≤n≤109,1≤m≤5,1≤pici≤1059​​,1m5,1pici​​​​105​​。

    最近一直在做CRT,乍一看还以为又是一个水题,实际上并没有那么简单。

    是时候来总结一下中国剩余定理CRT,并且开启奇妙的扩展卢卡斯定理ex_lucas了。

    审题,题意很简单,要求 Π(i=1->m)C(n-∑(j=1->i-1)wj,wj)%P

    组合数取模,很容易想到卢卡斯定理。

    然而根据题意,P并不一定是质数,普通卢卡斯定理只适用于质数。

    怎么办呢?那么你觉得我所说的ex_lucas是干什么用的呢?

    如果我们把P质因数分解,不就得到了质数吗?

    我们只需要求出ans%piki 然后再CRT解关于它们的余数方程,就好了呀。

      简单介绍CRT:

        对于余数方程xΞai(mod pi) 其中pi彼此互质。

        设P=∏pi,则最小自然数解x=(∑P/pi*ai*si)%P;其中si表示方程(P/pi)*x+pi*y=1的解。

        证明略。而对于pi不互质的情况需要ex_CRT,十分恶心不再这里介绍了。

     1 //tim[i]即为pi,aans[i]即为a[i],prr即为P
     2 int CRT(){
     3     int ans=0,x,y;
     4     for(int i=1;i<=nom;++i){
     5         ex_gcd(prr/tim[i],tim[i],x,y);
     6         x=(x%tim[i]+tim[i])%tim[i];
     7         ans=(ans+prr/tim[i]*aans[i]%prr*x%prr)%prr;
     8     }
     9     return ans;
    10 }
    CRT

    接下来我们继续考虑求ans%piki,下一个问题还是组合数如何对非质数取模。

    然而现在这个非质数不再是普通的合数了,而是一个质数的幂,即要求C(n,m)%pk

    那么过程中求阶乘时不能很轻易地求出它的逆元了,因为它本身就可能含有p这个因子。

    那还不简单?把阶乘的因子p都提出来不就好了嘛?

    则C(n,m)%pk=(n! / pk1)*inv(m! / pk2)*inv((n-m)! / pk3)*pk1+k2+k3

    现在inv内部的东西与p互质了,可以求逆元了。

    接下来再来考虑如何求(n! / pk1)%pk,除个pk1不是什么难事,先讨论n!%pk,如22!%32

    =1×2×3×4×5×6×7×8×9×10×11×12×13×14×15×16×17×18×19×20×21×22

    提出含p=3的项

    =37×(1×2×3×4×5×6×7)×(1×2×4×5×7×8×10×11×13×14×16×17×19×20×22)

    蓝色部分:是37,即pn/p,快速幂。

    绿色部分:是7!,即(n/p)!,递归解决。

    红色部分:是与p互质的数的乘积,其中以pk为分段可以发现前两各完整的段里有

        (1×2×4×5×7×8)Ξ(10×11×13×14×16×17) (mod pk)

        这样就有了一个循环节,快速幂解决。多余部分暴力乘出来就可以。

    1 int fac(int n,int p,int pt,int ans=1){
    2     if(!n)return 1;
    3     for(int i=1;i<pt;++i)if(i%p)ans=ans*i%pt;
    4     ans=pow(ans,n/pt,pt);
    5     for(int i=1;i<=n%pt;++i)if(i%p)ans=ans*i%pt;
    6     return ans*fac(n/p,p,pt)%pt;
    7 }
    整个n!%pk

    逐步回代,解决啦。

     1 #include<cstdio>
     2 #include<cmath>
     3 #define int long long
     4 using namespace std;
     5 int m,pr,sqr,modd[100005],tim[100005],aans[100005],p[100005],nop,num[6],nom,lef[6],prr;bool np[100005];
     6 int pow(int b,int t,int p,int ans=1){
     7     while(t){
     8         if(t&1)ans=ans*b%p;
     9         b=b*b%p;
    10         t/=2;
    11     }
    12     return ans;
    13 }
    14 int fac(int n,int p,int pt,int ans=1){
    15     if(!n)return 1;
    16     for(int i=1;i<pt;++i)if(i%p)ans=ans*i%pt;
    17     ans=pow(ans,n/pt,pt);
    18     for(int i=1;i<=n%pt;++i)if(i%p)ans=ans*i%pt;
    19     return ans*fac(n/p,p,pt)%pt;
    20 }
    21 void ex_gcd(int a,int b,int &x,int &y){
    22     if(!b){x=1;y=0;return;}
    23     ex_gcd(b,a%b,x,y);
    24     int res=x;x=y;y=res-a/b*y;
    25 }
    26 int inv(int n,int p){
    27     int x,y;
    28     ex_gcd(n,p,x,y);
    29     return (x%p+p)%p;
    30 }
    31 int c(int b,int t,int p,int pt){
    32     if(b<t)return 0;int cnt=0;
    33     for(int i=b;i;i/=p)cnt+=i/p;
    34     for(int i=t;i;i/=p)cnt-=i/p;
    35     for(int i=b-t;i;i/=p)cnt-=i/p;
    36     return fac(b,p,pt)*inv(fac(t,p,pt),pt)%pt*inv(fac(b-t,p,pt),pt)%pt*pow(p,cnt,pt)%pt;
    37 }
    38 int CRT(){
    39     int ans=0,x,y;
    40     for(int i=1;i<=nom;++i){
    41         ex_gcd(prr/tim[i],tim[i],x,y);
    42         x=(x%tim[i]+tim[i])%tim[i];
    43         ans=(ans+prr/tim[i]*aans[i]%prr*x%prr)%prr;
    44     }
    45     return ans;
    46 }
    47 int ex_lucas(){
    48     for(int i=1;i<=nop;++i){
    49         if(pr%p[i]==0)modd[++nom]=p[i],tim[nom]=1;
    50         while(pr%p[i]==0)pr/=p[i],tim[nom]*=p[i];
    51     }
    52     for(int ii=1;ii<=nom;++ii){
    53         aans[ii]=1;
    54         for(int i=1;i<=m;++i)aans[ii]=(aans[ii]*c(lef[i],num[i],modd[ii],tim[ii]))%tim[ii];
    55     }
    56     return CRT();
    57 }
    58 signed main(){
    59     scanf("%lld%lld%lld",&pr,&lef[1],&m);prr=pr;
    60     for(int i=1;i<=m;++i)scanf("%lld",&num[i]),lef[i+1]=lef[i]-num[i];
    61     if(lef[m+1]<0){puts("Impossible");return 0;}
    62     for(int i=2;i<=100000;++i){
    63         if(!np[i])p[++nop]=i;
    64         for(int j=1;j<=nop&&i*p[j]<=100000;++j)
    65             if(i%p[j])np[i*p[j]]=1;
    66             else{np[i*p[j]]=1;break;}
    67     }
    68     printf("%lld
    ",ex_lucas());
    69 }
    详情见代码
  • 相关阅读:
    log4Net使用
    VS Code入门
    用VS Code写Python
    C#(99):LINQ查询操作符实例
    C#(99):LINQ to Objects(2)
    spring mvc 配置对静态资源的访问
    EntLib 自动数据库连接字符串加密
    块级格式化上下文( Block formatting contexts)
    Entlib DAAB映射枚举类型
    js 继承
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11081666.html
Copyright © 2020-2023  润新知