题意:n件礼物,送给m个人,每人的礼物数确定,求方案数。

解题关键:由于模数不是质数,所以由唯一分解定理,

$\bmod  = p_1^{{k_1}}p_2^{{k_2}}......p_s^{{k_s}}$

然后,分别求出每个组合数模每个$p_i^{{k_i}}$的值,这里可以用扩展lucas定理求解,(以下其实就是扩展lucas定理的简略证明)

关于$C_n^m\% {p^k}$,

$C_n^m = \frac{{n!}}{{m!(n - m)!}}$,

我们以$n=19,p=3,k=2$为例,

$\begin{array}{l}
19! = 1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19\\
= (1*2*4*5*7*8*10*11*13*14*16*17*19)*36*(1*2*3*4*5*6)
\end{array}$

通过观察,我们可以将将上式分成三部分,

第一部分,3的幂,快速幂可以直接求解;

第二部分,$n!$项,可以递归求解;

第三部分,$(1*2*4*5*7*8*10*11*13*14*16*17*19)$,此项在模${3^2}$意义下是存在循环节${p^k}$的,可以暴力求出一个循环节,然后重复即可,最后一个循环节的长度一定小于${p^k}$,可以在不提升复杂度的基础上暴力。

那我们回归最初的问题,关于$\frac{{n!}}{{m!(n - m)!}}\bmod {p^k}$的求解,由于在模意义下牵扯到求逆元,而不互质是不存在逆元的,所以需将阶乘中与模数不互质的部分提取出来,而这一定是$p$的倍数。

$\frac{{n!}}{{m!(n - m)!}} = \frac{{\frac{{n!}}{{{p^{{k_1}}}}}*{p^{{k_1}}}}}{{\frac{{m!}}{{{p^{{k_2}}}}}*{p^{{k_2}}}*\frac{{(n - m)!}}{{{p^{{k_3}}}}}*{p^{{k_3}}}}} = \frac{{\frac{{n!}}{{{p^{{k_1}}}}}}}{{\frac{{m!}}{{{p^{{k_2}}}}}*\frac{{(n - m)!}}{{{p^{{k_3}}}}}}}*{p^{{k_1} - {k_2} - {k_3}}}\bmod {p^k}$,

而$\frac{{n!}}{{{p^{{k_1}}}}},\frac{{m!}}{{{p^{{k_2}}}}},\frac{{(n - m)!}}{{{p^{{k_3}}}}}$是与${p^k}$互质的,可以求逆元。

所以,我们只需求出每个阶乘的第二部分和第三部分,关于$p$的幂,直接将三个阶乘的结果求出即可。

这种方法可以扩展到任意阶乘模非质数的情况。

最后用中国剩余定理组合一下。

注意最后不同质因数之间是互质的,所以直接crt即可,不需扩展crt。

最终的解为$C_n^{n - w[1]}C_{n - w[1]}^{w[2]}C_{n - w[1] - w[2]}^{w[3]}......$

法一:组合数求解。

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<cstdlib>
typedef long long ll;
using namespace std;
ll mod,n,m,w[],ans,x,y,module[],piset[],r[],num; ll mod_pow(ll x,ll n,ll p){
ll res=;
while(n){
if(n&) res=res*x%p;
x=x*x%p;
n>>=;
}
return res;
} ll extgcd(ll a,ll b,ll &x,ll &y){
ll d=a;
if(b) d=extgcd(b,a%b,y,x),y-=a/b*x;
else x=,y=;
return d;
} ll inv(ll t,ll mod){ extgcd(t,mod,x,y);return (x+mod)%mod;} ll multi(ll n,ll pi,ll pk){//求非互质的部分
if (!n) return ;
ll ans=;
for (ll i=;i<=pk;i++) if(i%pi) ans=ans*i%pk;
ans=mod_pow(ans,n/pk,pk);
for (ll i=;i<=n%pk;i++) if(i%pi) ans=ans*i%pk;
return ans*multi(n/pi,pi,pk)%pk;
} ll exlucas(ll n,ll m,ll pi,ll pk){//组合数 c(n,m)mod pk=pi^k
if(m>n) return ;
ll a=multi(n,pi,pk),b=multi(m,pi,pk),c=multi(n-m,pi,pk);
ll k=;
for(ll i=n;i;i/=pi) k+=i/pi;
for(ll i=m;i;i/=pi) k-=i/pi;
for(ll i=n-m;i;i/=pi) k-=i/pi;
return a*inv(b,pk)%pk*inv(c,pk)%pk*mod_pow(pi,k,pk)%pk;//组合数求解完毕
} ll crt(int n,ll *r,ll *m){
ll M=,ret=;
for(int i=;i<n;i++) M*=m[i];
for(int i=;i<n;i++){
ll w=M/m[i];
ret+=w*inv(w,m[i])*r[i];
ret%=M;
}
return (ret+M)%M;
} ll fz(ll n,ll *m,ll *piset){//分解质因子
ll num=;
for (ll i=;i*i<=n;i++){
if(n%i==){
ll pk=;
while(n%i==) pk*=i,n/=i;
m[num]=pk;
piset[num]=i;
num++;
}
}
if(n>) m[num]=n,piset[num]=n,num++;
return num;
} ll excomb(ll n,ll m){
for(int i=;i<num;i++){
r[i]=exlucas(n,m,piset[i],module[i]);
}
return crt(num,r,module);
} int main(){
scanf("%lld",&mod);
scanf("%lld%lld",&n,&m);
ll sum=;
for(int i=;i<=m;i++) scanf("%lld",&w[i]),sum+=w[i];
if(n<sum){ puts("Impossible");return ;}//puts会自动换行
num=fz(mod,module,piset);
ans=;
for(int i=;i<=m;i++){
n-=w[i-];
ll a1=excomb(n,w[i]);
ans=ans*a1%mod;
}
printf("%lld\n",ans);
return ;
}

法二:多项式系数求解。

最终解为:

$\left( {\begin{array}{*{20}{c}}
n\\
{{w_1}{w_2}...{w_n}(n - sum)}
\end{array}} \right) = \frac{{n!}}{{{w_1}!{w_2}!...{w_m}!(n - sum)!}}$

 #include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<cstdlib>
typedef long long ll;
using namespace std;
ll mod,P,n,m,w[],ans,x,y,module[],piset[],r[],num,jc[]; ll mod_pow(ll x,ll n,ll p){
ll res=;
while(n){
if(n&) res=res*x%p;
x=x*x%p;
n>>=;
}
return res;
} ll extgcd(ll a,ll b,ll &x,ll &y){
ll d=a;
if(b) d=extgcd(b,a%b,y,x),y-=a/b*x;
else x=,y=;
return d;
} ll inv(ll t,ll mod){ extgcd(t,mod,x,y);return (x+mod)%mod;} ll multi(ll n,ll pi,ll pk){//求非互质的部分
if (!n) return ;
ll ans=;
for (ll i=;i<=pk;i++) if(i%pi) ans=ans*i%pk;
ans=mod_pow(ans,n/pk,pk);
for (ll i=;i<=n%pk;i++) if(i%pi) ans=ans*i%pk;
return ans*multi(n/pi,pi,pk)%pk;
} ll exlucas(ll n,ll pi,ll pk){
ll ans=multi(n,pi,pk);
for(int i=;i<m;i++){
jc[i]=multi(w[i+],pi,pk);
ans=ans*inv(jc[i],pk)%pk;
}
ll k=;
for(ll i=n;i;i/=pi) k+=i/pi;
for(int i=;i<=m;i++) for(ll j=w[i];j;j/=pi) k-=j/pi;
return ans*mod_pow(pi,k,pk)%pk;
} ll crt(int n,ll *r,ll *m){
ll M=,ret=;
for(int i=;i<n;i++) M*=m[i];
for(int i=;i<n;i++){
ll w=M/m[i];
ret+=w*inv(w,m[i])*r[i];
ret%=M;
}
return (ret+M)%M;
} ll fz(ll n,ll *m,ll *piset){//分解质因子
ll num=;
for (ll i=;i*i<=n;i++){
if(n%i==){
ll pk=;
while(n%i==) pk*=i,n/=i;
m[num]=pk;
piset[num]=i;
num++;
}
}
if(n>) m[num]=n,piset[num]=n,num++;
return num;
} int main(){
scanf("%lld",&mod);
scanf("%lld%lld",&n,&m);
ll sum=;
for(int i=;i<=m;i++) scanf("%lld",&w[i]),sum+=w[i];
if(n<sum){ puts("Impossible");return ;}//puts会自动换行
if (sum<n) w[++m]=n-sum;
num=fz(mod,module,piset);
for(int i=;i<num;i++) r[i]=exlucas(n,piset[i],module[i]);
printf("%lld\n",crt(num,r,module));
return ;
}

[bzoj2142]礼物(扩展lucas定理+中国剩余定理)的更多相关文章

  1. BZOJ - 2142 礼物 (扩展Lucas定理)

    扩展Lucas定理模板题(貌似这玩意也只能出模板题了吧~~本菜鸡见识鄙薄,有待指正) 原理: https://blog.csdn.net/hqddm1253679098/article/details ...

  2. BZOJ1951 [Sdoi2010]古代猪文 【费马小定理 + Lucas定理 + 中国剩余定理 + 逆元递推 + 扩展欧几里得】

    题目 "在那山的那边海的那边有一群小肥猪.他们活泼又聪明,他们调皮又灵敏.他们自由自在生活在那绿色的大草坪,他们善良勇敢相互都关心--" --选自猪王国民歌 很久很久以前,在山的那 ...

  3. hdu 5446(2015长春网络赛J题 Lucas定理+中国剩余定理)

    题意:M=p1*p2*...pk:求C(n,m)%M,pi小于10^5,n,m,M都是小于10^18. pi为质数 M不一定是质数 所以只能用Lucas定理求k次 C(n,m)%Pi最后会得到一个同余 ...

  4. [BZOJ2142]礼物(扩展Lucas)

    2142: 礼物 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 2286  Solved: 1009[Submit][Status][Discuss] ...

  5. Hdu 5446 Unknown Treasure (2015 ACM/ICPC Asia Regional Changchun Online Lucas定理 + 中国剩余定理)

    题目链接: Hdu 5446 Unknown Treasure 题目描述: 就是有n个苹果,要选出来m个,问有多少种选法?还有k个素数,p1,p2,p3,...pk,结果对lcm(p1,p2,p3.. ...

  6. BZOJ 1951: [Sdoi2010]古代猪文 [Lucas定理 中国剩余定理]

    1951: [Sdoi2010]古代猪文 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 2194  Solved: 919[Submit][Status] ...

  7. bzoj2142 礼物——扩展卢卡斯定理

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2142 前几天学了扩展卢卡斯定理,今天来磕模板! 这道题式子挺好推的(连我都自己推出来了) , ...

  8. BZOJ2142 礼物 扩展lucas 快速幂 数论

    原文链接http://www.cnblogs.com/zhouzhendong/p/8110015.html 题目传送门 - BZOJ2142 题意概括 小E购买了n件礼物,送给m个人,送给第i个人礼 ...

  9. 【Lucas组合数定理+中国剩余定理】Mysterious For-HDU 4373

    Mysterious For-HDU 4373 题目描述 MatRush is an ACMer from ZJUT, and he always love to create some specia ...

随机推荐

  1. Future Promise 模式(netty源码9)

    netty源码死磕9  Future Promise 模式详解 1. Future/Promise 模式 1.1. ChannelFuture的由来 由于Netty中的Handler 处理都是异步IO ...

  2. images have the “stationarity” property, which implies that features that are useful in one region are also likely to be useful for other regions.

    Convolutional networks may include local or global pooling layers[clarification needed], which combi ...

  3. 数据处理 数据入数据库 与 Excel

    Python  数据处理   中间数据 Excel   团队交流分工   低的沟通成本    数据入数据库 如postgresql

  4. ASP跳出FOR循环

    由于ASP不能使用GOTO语句,我在FOR循环中加入一个FOR循环,若需要跳出,即退出最里面那个FOR循环. DEMO: <%dim aa = 0for i = 1 to 10    for j ...

  5. LATEX ——WinEdt 破解

    WinEdt 是目前我发现最好的LaTeX编辑器,但是在国内支付不便,且学生许可需$40,只能出此下策,望有余力者尽量购买正版. WinEdt 的旧版本的破解方法众所周知,只需定时删除HKCU\Sof ...

  6. 路由器桥接(WIFI无线中继)设置及摆放位置图解

    路由器桥接(WIFI无线中继)设置及摆放位置图解 WIFI实在好用,但它的波覆盖面小.穿透力很差.我们安装时要考虑波的衍射特点,装在衍射效果最佳的位置(居中,室外可绕,避开密封墙).确实无法兼顾的地方 ...

  7. 关于align-items和align-content的区别和使用场景

    最近在研究flex布局,容器中有两个属性,是用来定义crossAxis测轴排列方式的.一开始接触align-items还可以理解感觉不难,后来看到align-content就感觉有点混淆了,特开一篇博 ...

  8. crontab定时任务写法记录

    基本格式 : * * * * * command 分 时 日 月 周 命令 第1列表示分钟1-59 每分钟用*或者 */1表示 第2列表示小时1-23(0表示0点) 第3列表示日期1-31 第4列表示 ...

  9. Data Structure Binary Tree: Construct Full Binary Tree from given preorder and postorder traversals

    http://www.geeksforgeeks.org/full-and-complete-binary-tree-from-given-preorder-and-postorder-travers ...

  10. [原创]Scala学习:编写Scala脚本

    scala支持脚本 1)在/opt/scala-script下创建一个文件hello.scala 编辑内容如下: $ hello ,this is the first scala script 2)运 ...