博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[国家集训队]礼物
阅读量:4982 次
发布时间:2019-06-12

本文共 2523 字,大约阅读时间需要 8 分钟。

题目大意:给出n和a[1]到a[m],求∑C(a[i],n-∑a[j](j<i))对非质数P取余的结果。

其实本题难点在于组合数对非质数取余

先了解一下普通lucas

(本人认为仅次于gcd的第二好写的数论板子)

lucas定理常用于组合数对质数取余,定理为:

 

C(n,m) ≡ C(n/p,m/p) * C(n%p,m%p) ( mod p )

理性理解一下就好。

所以我第一次用lucas+CRT拿了75

接下来进入正题,什么是拓展lucas?

拓展lucas的精髓在于递归地求解 n! % p^k (p是质数) 中国剩余定理 。(说实话和普通lucas没有一点关系

1.求解阶乘对质数整数次幂去模:

先举个例子:

n=22 , p = 3 , k = 2 , pk = p^k = 9

则:

n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20 * 21 * 22

其中黑的是p的倍数。

把黑的拉出来,是:3^7 * ( 1 * 2 * 3 * 4 * 5 * 6 * 7 );

其中括号里的是阶乘形式,递归向下做;

然后发现:

1 * 2 * 4 * 5 * 7 * 8 和 10 * 11 * 13 * 14 * 16 * 17 是同余的(mod pk)。

所以可以只算一组(从2到pk),然后快速幂n/pk次。

最后还剩 19 * 20 , 其实只要做 1 * 2就行了,反正同余。

2.CRT的应用:

因为1操作需要pk是p^k形式,所以要把原来的mod分解,然后分组做,可以得到一个线性同余方程组,

然后CRT合并。

代码:

#include
#include
using namespace std;#define ll long longint P,n,m,a[10];ll ans = 1;ll fast(ll x,int y,ll mod){ ll ret = 1ll; while(y) { if(y&1)ret=ret*x%mod; x=x*x%mod; y>>=1; } return ret;}void exgcd(ll aa,ll b,ll &x,ll &y){ if(!b) { x=1,y=0; return ; } exgcd(b,aa%b,y,x); y-=aa/b*x;}ll inv(ll aa,ll b){ ll x,y; exgcd(aa,b,x,y); x = (x%b+b)%b; return x;}ll stp(ll x,ll p,ll pk)//x! % p^k{ if(!x)return 1; ll ret = 1; for(int i=2;(ll)i<=pk;i++) if(i%p)ret=ret*i%pk; ret = fast(ret,x/pk,pk); for(int i=2;(ll)i<=x%pk;i++) if(i%p)ret=ret*i%pk; return ret*stp(x/p,p,pk)%pk;}ll C(ll x,ll y,ll M,ll p,ll pk){ ll a = stp(y,p,pk),b = stp(x,p,pk),c = stp(y-x,p,pk),k = 0; for(ll i=y;i;i/=p)k+=i/p; for(ll i=x;i;i/=p)k-=i/p; for(ll i=y-x;i;i/=p)k-=i/p; ll ret = a*inv(b,pk)%pk*inv(c,pk)%pk*fast(p,k,pk)%pk; return ret*(M/pk)%M*inv(M/pk,pk)%M;}ll p0[55],md[55],cnt;int main(){ scanf("%d%d%d",&P,&n,&m); int sum = 0; for(int i=1;i<=m;i++) { scanf("%d",&a[i]); sum+=a[i]; } if(sum>n) { printf("Impossible\n"); return 0; } ll M = P; for(int i=2;i*i<=P;i++) { if(P%i==0) { p0[++cnt] = i; md[cnt]=1; while(P%i==0)P/=i,md[cnt]*=i; } } if(P!=1) { p0[++cnt] = P; md[cnt] = P; } ll las = n,ans = 1; for(int i=1;i<=m;i++) { ll now = 0; for(int j=1;j<=cnt;j++) { now = (now+C(a[i],las,M,p0[j],md[j]))%M; } ans = ans*now%M; las-=a[i]; } printf("%lld\n",ans); return 0;}

 

转载于:https://www.cnblogs.com/LiGuanlin1124/p/9723801.html

你可能感兴趣的文章
js-控制浏览器和移动端的后退按钮 . popstate
查看>>
[QT][SQLITE]学习记录二 日期查询
查看>>
hdu 4455 Substrings
查看>>
web传参
查看>>
Python 查找binlog文件
查看>>
Git——如何将本地项目提交至远程仓库
查看>>
Convert CString to std::string
查看>>
3 - Selenium元素定位和操作
查看>>
GCC C语言 DLL范例,含源码
查看>>
冲刺第一天(补发)
查看>>
iOS开发Xcode中切换显示语言实现国际化
查看>>
C++模板学习
查看>>
nginx
查看>>
大数据平台搭建-hadoop集群的搭建
查看>>
安装一些包管理的记录 win10
查看>>
Android RecyclerView notifyDataSetChanged不起作用
查看>>
AndroidStudio3.0 Canary 8注解报错Annotation processors must be explicitly declared now.
查看>>
Android 一个改进的okHttp封装库
查看>>
genymotion下载出现Unable to create virtual device,Server returned HTTP status code 0.
查看>>
Android 下拉刷新框架实现
查看>>