• [国家集训队]礼物


    题目大意:给出n和a[1]到a[m],求∑C(a[i],n-∑a[j](j<i))对非质数P取余的结果。

    其实本题难点在于组合数对非质数取余

    先了解一下普通lucas

    (本人认为仅次于gcd的第二好写的数论板子)

    lucas定理常用于组合数对质数取余,定理为:

    C(n,m) ≡ C(n/p,m/p) * C(n%p,m%p) ( mod p )

    理性理解一下就好。

    所以我第一次用lucas+CRT拿了75

    接下来进入正题,什么是拓展lucas?

    拓展lucas的精髓在于递归地求解 n! % p^k (p是质数) 中国剩余定理 。(说实话和普通lucas没有一点关系

    1.求解阶乘对质数整数次幂去模:

    先举个例子:

    n=22 , p = 3 , k = 2 , pk = p^k = 9

    则:

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

    其中黑的是p的倍数。

    把黑的拉出来,是:3^7 * ( 1 * 2 * 3 * 4 * 5 * 6 * 7 );

    其中括号里的是阶乘形式,递归向下做;

    然后发现:

    1 * 2 * 4 * 5 * 7 * 8 和 10 * 11 * 13 * 14 * 16 * 17 是同余的(mod pk)。

    所以可以只算一组(从2到pk),然后快速幂n/pk次。

    最后还剩 19 * 20 , 其实只要做 1 * 2就行了,反正同余。

    2.CRT的应用:

    因为1操作需要pk是p^k形式,所以要把原来的mod分解,然后分组做,可以得到一个线性同余方程组,

    然后CRT合并。

    代码:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define ll long long
    int P,n,m,a[10];
    ll ans = 1;
    ll fast(ll x,int y,ll mod)
    {
        ll ret = 1ll;
        while(y)
        {
            if(y&1)ret=ret*x%mod;
            x=x*x%mod;
            y>>=1;
        }
        return ret;
    }
    void exgcd(ll aa,ll b,ll &x,ll &y)
    {
        if(!b)
        {
            x=1,y=0;
            return ;
        }
        exgcd(b,aa%b,y,x);
        y-=aa/b*x;
    }
    ll inv(ll aa,ll b)
    {
        ll x,y;
        exgcd(aa,b,x,y);
        x = (x%b+b)%b;
        return x;
    }
    ll stp(ll x,ll p,ll pk)//x! % p^k
    {
        if(!x)return 1;
        ll ret = 1;
        for(int i=2;(ll)i<=pk;i++)
            if(i%p)ret=ret*i%pk;
        ret = fast(ret,x/pk,pk);
        for(int i=2;(ll)i<=x%pk;i++)
            if(i%p)ret=ret*i%pk;
        return ret*stp(x/p,p,pk)%pk;
    }
    ll C(ll x,ll y,ll M,ll p,ll pk)
    {
        ll a = stp(y,p,pk),b = stp(x,p,pk),c = stp(y-x,p,pk),k = 0;
        for(ll i=y;i;i/=p)k+=i/p;
        for(ll i=x;i;i/=p)k-=i/p;
        for(ll i=y-x;i;i/=p)k-=i/p;
        ll ret = a*inv(b,pk)%pk*inv(c,pk)%pk*fast(p,k,pk)%pk;
        return ret*(M/pk)%M*inv(M/pk,pk)%M;
    }
    ll p0[55],md[55],cnt;
    int main()
    {
        scanf("%d%d%d",&P,&n,&m);
        int sum = 0;
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        if(sum>n)
        {
            printf("Impossible
    ");
            return 0;
        }
        ll M = P;
        for(int i=2;i*i<=P;i++)
        {
            if(P%i==0)
            {
                p0[++cnt] = i;
                md[cnt]=1;
                while(P%i==0)P/=i,md[cnt]*=i;
            }
        }
        if(P!=1)
        {
            p0[++cnt] = P;
            md[cnt] = P;
        }
        ll las = n,ans = 1;
        for(int i=1;i<=m;i++)
        {
            ll now = 0;
            for(int j=1;j<=cnt;j++)
            {
                now = (now+C(a[i],las,M,p0[j],md[j]))%M;
            }
            ans = ans*now%M;
            las-=a[i];
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    单链表
    找最长最短字符串
    注解
    Json字符串
    java的反射机制
    java类的加载与加载器
    String StringBuilder StringBuffer
    查看运行某类时当前的进程状态
    迷宫小游戏
    类的初始化过程、super关键字与函数绑定
  • 原文地址:https://www.cnblogs.com/LiGuanlin1124/p/9723801.html
Copyright © 2020-2023  润新知