zoukankan      html  css  js  c++  java
  • 【JZOJ3214】【SDOI2013】方程

    ╰( ̄▽ ̄)╭

    给定方程
    X1+X 2+…+Xn=m
    我们对第 1.. n1 个变量 进行一些限制 :
    X1≤A1
    X2≤A2

    Xn1 ≤An1
    我们对第 n1+1.. n1+1.. n1+ n2 个变量 进行一些限制 :
    X_(n1+1)≥A_(n1+1)
    X_(n1+2)≥A_(n1+2)

    X_(n1+n2) ≥A_(n1+n2)
    求:在满足这些限制的前提下, 该方程正整数解的个数。
    答案可能很大,请输出对 p取模 后的答案 ,也即 答案除以 p的余数。

    (⊙ ▽ ⊙)

    利用容斥原理,可以将所有要满足的条件一转化为条件二。
    而条件二可以利用隔板法,Ans=Cn1m1xi


    但是,本道题的最大Trick是组合数取模:Lucas定理+中国剩余定理

    两个定理

    中国剩余定理

    内容:
    a1,a2,a3...ak两两互质,且一个数X mod这些数,分别得到m1,m2,m3...,mk
    Mj=ni=1mi(i!=j)
    X=ni=1aiMiM1i

    Lucas定理

    内容:
    举个例子,比如说要算19! mod 32
    19!=1234...19
    把所有3的倍数提取出来:
    19!=(12457810111314161719)36(123456)
    很显然,(123456)可以利用相同的办法算出,
    (12457810111314161719)则可以发现,
    32属于一个循环节,于是可以使用快速幂优化。


    为了可以使用中国剩余定理
    我们先对题目所给的模数p分解质因数,得pkii
    对于Cmn,要对p取模;
    就让它先对每个pkii取模,这样就可以使用中国剩余定理


    问题又变成:Cmnpk取模了,
    Cmn=n!m!(nm!)
    所以可以对n!,m!,(nm)!分别使用Lucas定理
    n!t1pu1
    m!t2pu2
    (nm)!t3pu3
    最后,模出来的数即为pu1u2u3t1t21t31

    ( ̄~ ̄)

    #include<stdio.h>
    #define ll long long
    using namespace std;
    const ll maxn=110000;
    ll t,n,m,n1,n2,mo,i,j,k;
    ll a[maxn],ans,p[maxn],pp[maxn],P[maxn];
    ll ch[maxn],fac[maxn];
    ll qpower(ll a,ll b,ll mo){
        ll c=1;
        while (b){
            if (b&1) c=a*c%mo;
            a=a*a%mo;
            b>>=1;
        }
        return c;
    }
    ll exgcd(ll a,ll b,ll &x,ll &y){
        if (b==0){
            x=1;
            y=0;
            return a;
        }
        ll r=exgcd(b,a%b,x,y);
        ll t=x;
        x=y;
        y=t-a/b*y;
        return r;
    }
    ll ni(ll v,ll mo){
        ll x,y;
        exgcd(v,mo,x,y);
        return (x%mo+mo)%mo;
    }
    ll L;
    void lucas(ll n,ll p,ll P,ll &tmp,ll &u){
        if (n==0) return ; 
        ll i,k=n/P,l=1;
        u+=n/p;
        if (k){
            l=qpower(L,k,P);
        }//循环节计算
        tmp=tmp*l%P;
        for (i=k*P+1;i<=n;i++) if (i%p) tmp=tmp*i%P;//计算剩余部分
        lucas(n/p,p,P,tmp,u);
    }
    ll count(ll m,ll n,ll p,ll P){
        ll t1=1,t2=1,t3=1,u1=0,u2=0,u3=0;
        L=1;
        for (int i=1;i<=P;i++) if (i%p) L=L*i%P;
        lucas(n,p,P,t1,u1);
        lucas(m,p,P,t2,u2);
        lucas(n-m,p,P,t3,u3);
        return qpower(p,u1-u2-u3,P)*t1%P*ni(t2,P)%P*ni(t3,P)%P;
    }
    ll china(){
        ll i,j=0;
        for (i=1;i<=p[0];i++){
            ll tmp=mo/P[i];
            j=(j+ch[i]*ni(tmp,P[i])*tmp)%mo;
        }
        return (j%mo+mo)%mo;
    }
    ll c(ll up,ll down){
        ll i,j,k;
        if (up>down) return 0;
        for (i=1;i<=p[0];i++) ch[i]=count(up,down,p[i],P[i]);
        return china();
    }
    void solve(ll v,ll l,ll num){
        ll i,j,k;
        if (l>n1){
            ans=(ans+c(n-1,v-1)*(num&1?-1:1))%mo;
            return;
        }
        if (v-a[l]>0) solve(v-(a[l]),l+1,num+1);
        solve(v,l+1,num);
    }
    void init(){
        ll i,j,k=mo;
        for (i=2;i*i<=k;i++){
            if (k%i==0){
                p[++p[0]]=i;
                P[p[0]]=1;
                while (k%i==0){
                    k/=i;
                    pp[p[0]]++;
                    P[p[0]]*=i;
                }
            }
        }
        if (k>1){
            p[++p[0]]=k;
            pp[p[0]]=1;
            P[p[0]]=k;
        }
    }
    int main(){
        scanf("%lld%lld",&t,&mo);
        init();
        while (t--){
            scanf("%lld%lld%lld%lld",&n,&n1,&n2,&m);
            ans=0;
            for (i=1;i<=n1;i++) scanf("%d",&a[i]);
            for (i=1;i<=n2;i++) scanf("%d",&j),m-=j-1;
            solve(m,1,0);
            ans=(ans%mo+mo)%mo;
            printf("%lld
    ",ans);
        }
    }

    (⊙v⊙)

    1.使用Lucas定理时,可以预处理阶乘;
    2.使用Lucas定理时,要另开变量存储循环节的快速幂。

  • 相关阅读:
    [51nod] 1088 最长回文子串 #Hash+二分
    [51nod] 1378 夹克老爷的愤怒 #树形DP
    [BZOJ] 2456: mode #众数计数法
    [51nod] 1199 Money out of Thin Air #线段树+DFS序
    [51nod] 1494 选举拉票 #算法设计策略
    [51nod] 1463 找朋友 #离线+扫描线
    [BZOJ] 2733: [HNOI2012]永无乡 #线段树合并+并查集
    [BZOJ] 1012: [JSOI2008]最大数maxnumber
    [Codeforces] E. Lomsat gelral #DSU on Tree
    [BZOJ] 4756: [Usaco2017 Jan]Promotion Counting #线段树合并+权值线段树
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6714791.html
Copyright © 2011-2022 走看看