8.【题解】礼物

题解

\(\text{exlucas}\)\(\text{恶心lucas}\)

  • 首先 \(exlucas\)\(lucas\) 没半毛钱关系……
  • \(exlucas\) 的思路是将不一定是质数的模数 \(P\) 分解质因数 \(\Large P=p_1^{c_1}\times p_2^{c_2}\cdots p_n^{c_n}\) ,然后分别进行计算,最后用 \(crt\) 合并出最小结果。
  • 但是 \(\large p^c\) 不一定是质数,所以不能够用 \(lucas\) 计算(所以没半毛钱关系)。只有质数才一定有乘法逆元。
  • 首先 \(\large\dbinom{n}{m}=\dfrac{n!}{m!(n-m)!}\) 。所以 \(\large\dbinom{n}{m}=\dfrac{\dfrac{n!}{p^x}}{\dfrac{m!}{p^y}\dfrac{(n-m)!}{p^z}}\times p^{x-y-z}\) ,其中 \(x\)\(y\)\(z\) 分别为 \(n!\)\(m!\)\((n-m)!\)\(p\) 的质因子次数。
  • 当其与 \(p\) 互质后,就可以求逆元了。我们只要大力求出 \(p\) 的次数,就能够计算出 \(\dbinom{n}{m}\mod p\) 的值,枚举时间复杂度为 \(O(\log{n})\)
  • 于是我们就要着手于求 \(\LARGE\frac{n!}{p^x}\)\(\LARGE\frac{m!}{p^y}\)\(\LARGE\frac{(n-m)!}{p^z}\) 三个柿子的值。可以抽象为 \(\LARGE\frac{n!}{p^x}\)
  • 对于 \(n!\) 来说,将其拆开得到 \(1\times 2\times 3\cdots(n-2)(n-1)n\) 。如果 \(n\ge p\) 则取模后结果为 \(0\) ,否则可以转化为 \((p\times 2p\times 3p\cdots)(1\times 2\times 3\times\cdots)\)
    其中左边是 \(p\) 的倍数,有 \(\Large\lfloor\frac{n}{p}\rfloor\) 项,右边与 \(p\) 互质,有 \(n-\Large\lfloor\frac{n}{p}\rfloor\)(都小于 \(n\) )。
  • 所以我们可以将左边的 \(p\) 提出来, 得到
    \(\large p^{\lfloor\dfrac{n}{p}\rfloor}(1\times 2\times 3\cdots)(1\times 2\times 3\times\cdots)\) 等于 \(\large p^{\lfloor\frac{n}{p}\rfloor}(\lfloor\frac{n}{p}\rfloor)!\prod\limits_{\small i=1,i\not\equiv 0\pmod p}^{\small n}i\)
  • 将最后的一串乘法柿子提出循环部分(小于 \(n-\lfloor\frac{n}{p}\rfloor\) 的项)得出 \(\large p^{\lfloor\frac{n}{p}\rfloor}(\lfloor\frac{n}{p}\rfloor)!(\prod\limits_{\small i=1,i\not\equiv 0\pmod p}^{\small p^x}i)^{\large\lfloor\frac{n}{p^x}\rfloor}\prod\limits_{\small i=p^k\lfloor\dfrac{n}{p}\rfloor,i\not\equiv 0\pmod p}^{n}i\)
  • 显然 \(\large(\prod\limits_{\small i=1,i\not\equiv 0\pmod p}^{\small p^x}i)^{\large\lfloor\frac{n}{p^x}\rfloor}\) 是循环节, \(\large\prod\limits_{\small i=p^k\lfloor\dfrac{n}{p}\rfloor,i\not\equiv 0\pmod p}^{n}i\) 是剩余部分,不能组成循环节。
  • 很多地方都用 \(22!\) 举例。

\[\large 22!\equiv(1\times 2\times 4\times 5\times 7\times 8)(10\times 11\times 13\times 14\times 16\times 17)\\(19\times 20\times 22)(3\times 6\times 9\times 12\times 15\times 18\times 21)\pmod 3\\22! \equiv(1\times 2\times 4\times 5\times 7\times 8)^2(19\times 20\times 22)\\3^7(1\times 2\times 3\times 4\times 5\times 6\times 7)\pmod 3\\22! \equiv 3^77!(1\times 2\times 4\times 5\times 7\times 8)^2(19\times 20\times 22)\pmod 3 \]

  • 如果你问为啥 \((10\times 11\times 13\times 14\times 16\times 17)\) 能直接转化成 \((1\times 2\times 4\times 5\times 7\times 8)\) ,别忘了后面的取模, \((a\times b)\mod p=(a\mod p\times b\mod p)\mod p\) ,所以 \((10\times 11\times 13\times 14\times 16\times 17)\) 就等于 \(\\((1\mod 3^2\times 10\mod 3^2)\times \\ (1\mod 3^2\times 11\mod 3^2)\times \\(1\mod 3^2\times 13\mod 3^2)\times \\(1\mod 3^2\times 14\mod 3^2)\times \\(1\mod 3^2\times 16\mod 3^2)\times \\(1\mod 3^2\times 17\mod 3^2))\mod 3^2\)\(\\\) 等于 \((\large 1\times 2\times 4\times 5\times 7\times 8)\)
  • 显然 \(\large p^{\lfloor\frac{n}{p}\rfloor}(\lfloor\frac{n}{p}\rfloor)!(\prod\limits_{\small i=1,i\not\equiv 0\pmod p}^{\small p^x}i)^{\large\lfloor\frac{n}{p^x}\rfloor}\prod\limits_{\small i=p^k\lfloor\dfrac{n}{p}\rfloor,i\not\equiv 0\pmod p}^{n}i\) 中的 \(\large p^{\lfloor\frac{n}{p}\rfloor}\) 要除掉,但是我们却不能保证 \(\large(\lfloor\frac{n}{p}\rfloor)!\) 中不包含 \(p\)
  • 所以递归解决。定义 \(\large f(n)=\dfrac{n!}{p^x}\) ,所以 \(\large f(n)=f(\lfloor\frac{n}{p}\rfloor)(\prod\limits_{\small i=1,i\not\equiv 0\pmod p}^{\small p^x}i)^{\large\lfloor\frac{n}{p^x}\rfloor}\prod\limits_{\small i=p^k\lfloor\dfrac{n}{p}\rfloor,i\not\equiv 0\pmod p}^{n}i\) ,将可能包含因子 \(p\) 的柿子留到下一次递归计算,不含 \(p\) 的直接计算,递归结束后结果将不含因子 \(p\) ,可以用 \(\large\log_p(n)\) 的时间复杂度解决。
  • 因此原式等于 \(\dfrac{f(n)}{f(m)(f(n-m))}p^{x-y-z}\pmod {p^k}\)
  • 最后再求 \(p^{x-y-z}\) ,我们需要求出 \(\large p^{\lfloor\frac{n}{p}\rfloor}\) ,以及 \(\large (\lfloor\frac{n}{p}\rfloor)!\) 也不能忽视,所以定义 \(\large g(n)=\lfloor\frac{n}{p}\rfloor+g(\lfloor\frac{n}{p}\rfloor)\) 算出 \(p\) 的次数,时间复杂度也是 \(\large\log_p(n)\)
  • 所以最后就是求出 \(\large\dfrac{f(n)}{f(m)(f(n-m))}p^{g(n)-g(m)-g(n-m)}\pmod {p^k}\) 再用 \(crt\) 合并一下就可以了。

思路

  • 应该不是很抽象。
  • 实际上调 \(exlucas\) 才是最恶心的。
  • 这题显然就是求 \(\Large\sum\limits_{i=1}^{m}{\Large\dbinom{n-\sum\limits_{j=1}^{i}gift_i}{gift_i}} \mod p\) 所以用 \(exlucas\) 就可以像板子一样切掉此题……

代码

#include<bits/stdc++.h>
#define N (10000010)
#define int long long
using namespace std;
namespace IO
{
    #define ll long long
    const int MAX=1<<25;
    char buf[MAX],*p1=buf,*p2=buf;
    char obuf[MAX],*o=obuf;
    #define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    //template<typename T>
    //inline T read()
    inline int read()
    {
        int x=0;bool f=1;
        char c=gc();
        for(;c<48||c>57;c=gc())if(c=='-')f=0;
        for(;c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c^48);
        return f?x:~x+1;
    }
    void print(ll x){if(x>9)print(x/10);*o++=(x%10)+'0';}
    void pit(ll x){if(x<0)*o++='-',x=~x+1;print(x);}
    void write(ll x,char end){pit(x);*o++=end;}
    void flush(){fwrite(obuf,o-obuf,1,stdout);}
    #undef ll
}
using IO::read;using IO::write;using IO::flush;
int n,m,t,P;
int x,y,k,ans,possible,lon;
int mod[N],a[N],gt[N],nth_mod[N];
int len,prime[700010],phi[N];//线性筛欧拉函数
short mu[N];
bitset<N>vis;
int jc[N];
long long inv[N];//乘法逆元
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
inline void swap(int &x,int &y){int tmp=x;x=y;y=tmp;}
int e[N],siz[N];
long long qpow(long long x,int b,int P=P)
{
    long long ans=1;
    for(;b;b>>=1){if(b&1)ans=(ans*x)%P;x=(x*x)%P;}
    return ans;
}//O(log(b))
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int exgcd(int a,int b,int &x,int &y)
{
    if(!b){x=1,y=0;return a;}
    int d=exgcd(b,a%b,y,x);
    y-=(a/b*x);
    return d;
}//O(max(a,b))
int ola(int n)
{
    int ans=n;
    for(int i=2;i*i<=n;++i)
    {
        if(n%i==0)ans=ans/i*(i-1);
        for(;n%i==0;n/=i);
    }
    if(n>1)ans=ans/n*(n-1);
    return ans;
}//O(sqrt(n))
void eular(int n)//欧拉筛
{
    //memset(vis,0,sizeof(vis));
    phi[1]=1;
    for(int i(2);i<=n;++i)
    {
        if(!vis[i])
            prime[++len]=i,phi[i]=(i-1);
        for(int j(1);j<=len&&i*prime[j]<=n;++j)
        {
            vis[i*prime[j]]=1;
            if(!(i%prime[j]))
                {phi[i*prime[j]]=(phi[i]*prime[j]);break;}
            else phi[i*prime[j]]=(phi[i]*(prime[j]-1));
        }
    }
}//O(n)
void mobius(int n)
{
    //memset(vis,0,sizeof(vis));
    mu[1]=1;
    for(int i(2);i<=n;++i)
    {
        if(!vis[i])prime[++len]=i,mu[i]=-1;
        for(int j(1);j<=len&&i*prime[j]<=n;++j)
        {
            vis[i*prime[j]]=1;
            if(!(i%prime[j])){mu[i*prime[j]]=0;break;}
            mu[i*prime[j]]=~mu[i]+1;
        }
    }
}//O(n)
void eular_mobius(int n)
{
    //memset(vis,0,sizeof(vis));
    mu[1]=phi[1]=1;
    for(int i(2);i<=n;++i)
    {
        if(!vis[i])prime[++len]=i,phi[i]=(i-1),mu[i]=-1;
        for(int j(1);j<=len&&i*prime[j]<=n;++j)
        {
            vis[i*prime[j]]=1;
            if(!(i%prime[j]))
            {
                phi[i*prime[j]]=(phi[i]*prime[j]);
                mu[i*prime[j]]=0;
                break;
            }
            phi[i*prime[j]]=(phi[i]*(prime[j]-1));
            mu[i*prime[j]]=~mu[i]+1;
        }
    }
}
void niyuan1(int n,int P=P)//乘法逆元
{
    inv[1]=1;
    for(int i(2);i<=n;++i)inv[i]=((P-P/i)*inv[P%i])%P;
}//O(n)
int inv_it(int a,int P=P)//O(log(a))
{
    int d(exgcd(a,P,x,y));
    return(x%P+P)%P;
}
int C(int n,int m,int P=P)
{
    if(m>n)return 0;
    int a(1),b(1);
    for(int i(n-m+1);i<=n;++i)a=(a*i)%P;
    for(int i(2);i<=m;++i)b=(b*i)%P;
    return(a*qpow(b,P-2,P))%P;
}
int lucas(int n,int m,int P=P)
{return(!m)?1:(C(n%P,m%P,P)*lucas(n/P,m/P,P))%P;}
int excrt(int n)//扩展中国剩余定理
{
    int mul(mod[1]);ans=a[1];
    int x,y,c,d;
    for(int i(2);i<=n;++i)
    {
        x=y=0;
        c=(a[i]-ans%mod[i]+mod[i])%mod[i];
        d=exgcd(mul,mod[i],x,y);
        if(!(c%d))
            ans+=(((x+mod[i])%mod[i])*(c/d)%mod[i])*mul,
            mul=mul*mod[i]/gcd(mul,mod[i]),
            ans%=mul;
        else return -1;
    }
    return ans%mul;
}
int exlucas_jc(int n,int mod,int P=P)
{
    if(!n)return 1;
    int res(1);
    for(int i(1);i<=P;++i)
        if(i%mod)res=(res*i)%P;
    res=qpow(res,n/P,P);
    for(int i(1);i<=n%P;++i)if(i%mod)res=(res*i)%P;
    return(res*(exlucas_jc(n/mod,mod,P)))%P;
}
int cal(int n,int m,int mod,int P=P)
{
    int c1(exlucas_jc(n,mod,P));
    int c2(exlucas_jc(m,mod,P));
    int c3(exlucas_jc(n-m,mod,P));
    int cnt(0);
    for(int i(n);i;i/=mod)cnt+=(i/mod);
    for(int i(m);i;i/=mod)cnt-=(i/mod);
    for(int i(n-m);i;i/=mod)cnt-=(i/mod);
    return(((qpow(mod,cnt,P)*c1)%P*inv_it(c2,P))%P*inv_it(c3,P))%P;
}
inline int crt(int x,int mod,int P)
{
    return inv_it(P/mod,mod)*(P/mod)*x;
}
int exlucas(int n,int m,int now,int P)
{return(crt(cal(n,m,nth_mod[now],mod[now]),mod[now],P));}
void init()
{
    P=read();
    n=read(),m=read();
    for(int i(1);i<=m;++i)gt[i]=read(),possible+=gt[i];
    if(possible>n)puts("Impossible"),exit(0);
    int PP(sqrt(P)),lsP(P);
    for(int i(2);i<=PP;++i)
    {
        if(!(lsP%i))mod[++lon]=i,lsP/=i,nth_mod[lon]=i;
        for(;!(lsP%i);lsP/=i)mod[lon]*=i;
    }//分解"只因"数
    if(lsP>1)mod[++lon]=lsP,nth_mod[lon]=lsP;//存在大于sqrt{n}的"只因"子
}
signed main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    init();
    int sum(0),num(0);ans=1;
    for(int i(1);i<=m;++i)
    {
        num=0;
        for(int j(1);j<=lon;++j)
            num=((num+exlucas(n-sum,gt[i],j,P)))%P;
        ans=(ans*num)%P;
        sum+=gt[i];
    }
    write(ans,' ');
    flush();
    return 0;
}
posted @ 2024-01-30 10:43  minecraft114514  阅读(22)  评论(2编辑  收藏  举报