• bzoj 2142


    数论大集合

    只要你做完了这道题,除了线性筛和降幂公式以外,所有数论noip知识点就都会了...

    题意:求C(n,∑w)*C(∑w,w1)*C(∑w-w1,w2).....mod p(不保证p为质数)

    思想:拓展卢卡斯定理

    算法:我们可以分别求每个C(n,m),然后乘起来mod p即可

    在求每个C(n,m)时,由公式C(n,m)=	frac{n!}{m!(n-m)!}

    于是:C(n,m)=	frac{n!}{m!(n-m)!}=n! mod p*inv(m!)*inv((n-m)!)

    于是我们仅需求出n!mod p的值

    可是首先,由于p不是质数,所以不能线性筛逆元

    而且,即使p是质数,由于n的范围过大,筛出来也T了

    所以我们要采用一些别的方法:

    p=p1^{k1}*p2^{k2}*p3^{k3}*...*pn^{kn}

    于是我们可以求出原式模每个pm^{km}的值(或逆元)

    然后应用中国剩余定理合并

    这就解决了第一个问题

    至于第二个问题,我们举例来说明一下:

    求19!mod 9的值(经典样例,来自popoqqq)

    19!mod 9=19*18*17*16...*1mod 9=(1*2*4*5*7*8*10*11*13*14*16*17*19)mod 9*3^6 mod 9*(1*2*3*4*5*6) mod 9

    对于第一部分,显然,1,2,4,5,7,8和10,11,13,14,16,17会构成一个对9的剩余系,那么这堆东西可以用快速幂来做,因为就是一个剩余系

    对于第二部分,我们仅需记录一下幂次,然后除掉下面的再处理(别忘了C(n,m)是有除法的,算出来取完模一堆0好像不太妙啊...)

    对于第三部分,发现还是一个阶乘,那我们递归处理即可

    最后,用中国剩余定理合并,就完事了

    代码:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define ll long long
    using namespace std;
    ll w[10];
    ll n,m,p;
    ll p0[25],pu[25],nu[25],cnt;
    ll ret[25];
    ll a[25];
    ll tot=0;
    struct node
    {
        ll mi;
        ll val;
    };
    void destroy()
    {
        int temp=p;
        for(int i=2;i*i<=temp;i++)
        {
            if(temp%i==0)
            {
                p0[++cnt]=i;
                pu[cnt]=i;
                nu[cnt]=1;
                temp/=i;
                while(temp%i==0)
                {
                    pu[cnt]*=i;
                    nu[cnt]++;
                    temp/=i;
                }
            }
        }
        if(temp!=1)
        {
            p0[++cnt]=temp;
            pu[cnt]=temp;
            nu[cnt]=1;
        }
    }
    ll gcd(ll x,ll y)
    {
        if(y==0)
        {
            return x;
        }
        return gcd(y,x%y);
    }
    void ex_gcd(ll a,ll b,ll &x,ll &y)
    {
        if(b==0)
        {
            x=1;
            y=0;
            return;
        }
        ex_gcd(b,a%b,x,y);
        ll t=x;
        x=y;
        y=t-(a/b)*x;
    }
    ll get_inv(ll x,ll y)
    {
        ll xx,yy;
        ex_gcd(x,y,xx,yy);
        return ((xx%y)+y)%y;
    }
    ll pow_mul(ll x,ll y,ll mod)
    {
        ll ans=1;
        while(y)
        {
            if(y%2)
            {
                ans*=x;
                ans%=mod;
            }
            x*=x;
            x%=mod;
            y/=2;
        }
        return ans;
    }
    node getmul(ll x,ll num)
    {
        if(x==0)
        {
            node ret;
            ret.mi=0;
            ret.val=1;
            return ret;
        }
        ll ans=1;
        ll p1=x/p0[num],p2=x/pu[num];
        if(p2)
        {
            for(int i=2;i<pu[num];i++)
            {
                if(i%p0[num])
                {
                    ans*=i;
                    ans%=pu[num];
                }
            }
            ans=pow_mul(ans,p2,pu[num]);
        }
        for(int i=p2*pu[num]+1;i<=x;i++)
        {
            if(i%p0[num])
            {
                ans*=i;
                ans%=p;
            }
        }
        node re=getmul(p1,num);
        node temp;
        temp.mi=re.mi+x;
        temp.val=ans*re.val%p;
        return temp;
    }
    ll C(ll x,ll y,ll num)//C(x,y)=x!/(y!(x-y)!)
    {
        if(x<y)
        {
            return 0;
        }
        node f1=getmul(x,num);
        node f2=getmul(y,num);
        node f3=getmul(x-y,num);
        ll t1=pow_mul(p0[num],f1.mi-f2.mi-f3.mi,pu[num])%pu[num];
        ll t2=f1.val*get_inv(f2.val,pu[num])%pu[num];
        ll t3=get_inv(f3.val,pu[num])%pu[num];
        return t1*t2%pu[num]*t3%pu[num];
    }
    ll china()
    {
        ll M=p;
        ll ans=0;
        for(int i=1;i<=cnt;i++)
        {
            ll M0=M/pu[i];
            ll xx,yy;
            ex_gcd(M0,pu[i],xx,yy);
            ans+=xx*M0%p*a[i]%p;
            ans%=p;
        }
        return (ans%M+M)%M;
    }
    ll solve(ll x,ll y)
    {
        for(int i=1;i<=cnt;i++)
        {
            a[i]=C(x,y,i);
        }
        return china();
    }
    int main()
    {
        scanf("%lld%lld%lld",&p,&n,&m);
        ll s=0;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&w[i]);
            s+=w[i];
        } 
        if(s>n)
        {
            printf("Impossible
    ");
            return 0;
        }
        destroy();
        ll re=solve(n,s);
        for(int i=1;i<=m;i++)
        {
            re*=solve(s,w[i]);
            re%=p;
            s-=w[i];
        }
        printf("%lld
    ",re);
        return 0;
    }
  • 相关阅读:
    MessageBoxButtons.OKCancel的选择事件
    Markdown 学习
    【Python】tesserocr的Path错误
    【Python套接字】socket编程
    【Python数据】懒人修仙传数值
    【Python画画】失败案例总结
    【Python画画】.ui文件转.py文件
    【Python截图】截图处理
    【想法】想做一个辅助工具
    【Python爬虫】从html里爬取中国大学排名
  • 原文地址:https://www.cnblogs.com/zhangleo/p/9719534.html
Copyright © 2020-2023  润新知