正题
题目链接:https://www.luogu.com.cn/problem/P4640
题目大意
nnn种物品,其中ttt种物品是有个数限制的,第iii种限制为bib_ibi,求选出mmm个物品的方案数%p\% p%p的值
1≤n,m,bi≤109,0≤t≤15,p∈[1,105]∩Pri1\leq n,m,b_i\leq 10^9,0\leq t\leq 15,p\in[1,10^5]\cap Pri1≤n,m,bi≤109,0≤t≤15,p∈[1,105]∩Pri
解题思路
看上去就很OGF\text{OGF}OGF的题目?
对于有限制的物品为f(x)=1−xbi+11−xf(x)=\frac{1-x^{b_i+1}}{1-x}f(x)=1−x1−xbi+1,其他都是f(x)=11−xf(x)=\frac{1}{1-x}f(x)=1−x1。
然后nnn个乘起来的话然后求前mmm次项的系数。
分子因为只有ttt个有值,直接暴力乘起来。下面那个分母是1(1−x)n\frac{1}{(1-x)^n}(1−x)n1
所以对于上面如果有一个axm−kax^{m-k}axm−k那么就会产生贡献
a∑i=0k(n+i−1n)=a(n+kn)a\sum_{i=0}^{k}\binom{n+i-1}{n}=a\binom{n+k}{n}ai=0∑k(nn+i−1)=a(nn+k)
(相等于选出0∼k0\sim k0∼k个球放进nnn个盒子里,开一个垃圾箱把没用的球放进去就好了)
然后模数小,开LucasLucasLucas就好了
时间复杂度O(2tlogpn)O(2^t\log_p n)O(2tlogpn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10;
ll n,t,m,p,b[20],ans,inv[N],fac[N];
ll C(ll n,ll m)
{return fac[n]*inv[m]%p*inv[n-m]%p;}
ll L(ll n,ll m){if(n<m)return 0;if(n<p)return C(n,m);return L(n/p,m/p)*L(n%p,m%p)%p;
}
signed main()
{scanf("%lld%lld%lld%lld",&n,&t,&m,&p);for(ll i=0;i<t;i++)scanf("%lld",&b[i]),b[i]++;inv[1]=1;for(ll i=2;i<p;i++)inv[i]=p-inv[p%i]*(p/i)%p;inv[0]=fac[0]=1;for(ll i=1;i<p;i++)fac[i]=fac[i-1]*i%p,inv[i]=inv[i-1]*inv[i]%p;ll MS=(1<<t); for(ll s=0;s<MS;s++){ll f=1,sum=0;for(ll i=0;i<t;i++)if((s>>i)&1)f=-f,sum+=b[i];(ans+=L(n+m-sum,n)*f)%=p;}printf("%lld\n",(ans+p)%p);return 0;
}