题解 P2183 【[国家集训队]礼物】

VenusM1nT

2019-08-26 21:31:39

Solution

扩展卢卡斯。 这题相当简单啊……唯一的难度就是在 $\texttt{ExLucas}$ 上了吧…… 我们需要将 $\text{sum}$ 个礼物分给 $n$ 个人,每个人需要拿的礼物数为 $a_i$,由于每个礼物不同,要求方案数,容易想到组合数学。 我们考虑一道小学奥数题:$7$ 个人中选 $3$ 个拍照,方案数是 $7\times 6\times 5$,而它的具体意义是:考虑第一个位置能选谁,方案数是 $7$,再考虑第二个位置,由于第一个位置已经放了一个人,所以方案数是 $6$,同理,第三个位置方案数为 $5$,然后我们再使用乘法原理将它们合并。同理,我们对这道题使用相同的思想进行拆解,考虑第一个人选礼物的方案数,是 $n$ 个里选 $a_1$ 个,即 $\binom{n}{a_1}$,再考虑第二个,此时我们还剩下 $n-a_1$ 个礼物,选 $a_2$ 个,方案数为 $\binom{n-a_1}{a_2}$,以此类推,第三个的方案数为 $\binom{n-a_1-a_2}{a_3}$。至此,我们已经得出了此题的做法。 令 $$\text{sum}_i=\sum_{j=1}^{i}a_i$$ 则答案为 $$\text{ans}=\prod_{i=1}^{m}\binom{n-\text{sum}_ {i-1}}{a_i}\ \text{mod}\ p$$ 使用 $\texttt{ExLucas}$ 即可解出。 ```cpp #include<bits/stdc++.h> #define MAXN 15 #define reg register #define inl inline #define int long long using namespace std; template <typename T> inl void Read(reg T &x) { x=0; reg int fu=1; reg char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') fu=-1; for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+(ch-48); x*=fu; } void ExGcd(reg int a,reg int b,reg int &x,reg int &y) { if(!b) { x=1; y=0; return; } ExGcd(b,a%b,x,y); reg int t=x; x=y; y=t-a/b*y; } int Gcd(reg int x,reg int y) { return !y?x:Gcd(y,x%y); } inl int Inv(reg int a,reg int b) { reg int x,y; ExGcd(a,b,x,y); return (x%b+b)%b; } inl int Pow(reg int x,reg int y,reg int p) { reg int res=1; for(;y;y>>=1,x=x*x%p) if(y&1) res=res*x%p; return res; } inl int Crt(reg int x,reg int y,reg int p) { return Inv(y/p,p)*(y/p)*x; } int Fac(reg int x,reg int a,reg int p) { if(!x) return 1; reg int res=1; for(reg int i=1;i<=p;i++) if(i%a) res=res*i%p; res=Pow(res,x/p,p); for(reg int i=1;i<=x%p;i++) if(i%a) res=res*i%p; return res*Fac(x/a,a,p)%p; } inl int C(reg int n,reg int m,reg int a,reg int p) { reg int x=Fac(n,a,p),y=Fac(m,a,p),z=Fac(n-m,a,p),sum=0; for(reg int i=n;i>=1;i/=a) sum+=i/a; for(reg int i=m;i>=1;i/=a) sum-=i/a; for(reg int i=n-m;i>=1;i/=a) sum-=i/a; return x*Pow(a,sum,p)%p*Inv(y,p)%p*Inv(z,p)%p; } inl int ExLucas(reg int n,reg int m,reg int p) { if(n<m) return 0; reg int t=p,res=0; for(reg int i=2;i*i<=p;i++) { reg int x=1; while(!(t%i)) { x*=i; t/=i; } res=(res+Crt(C(n,m,i,x),p,x))%p; } if(t>1) res=(res+Crt(C(n,m,t,t),p,t))%p; return res%p; } int n,m,p,a[MAXN],sum[MAXN],ans=1; signed main() { reg int sm=0; Read(p); Read(n); Read(m); for(reg int i=1;i<=m;i++) { Read(a[i]); sm+=a[i]; sum[i]=sum[i-1]+a[i]; } if(sm>n) return puts("Impossible"),0; for(reg int i=1;i<=m;i++) ans=ans*ExLucas(n-sum[i-1],a[i],p)%p; printf("%lld\n",ans); return 0; } ```