1648:【例 1】「NOIP2011」计算系数
第一种方法:直接用杨辉三角求出二项式系数
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int maxn=1005; typedef long long LL; int c[maxn][maxn]; int n,m,a,b,k; int mod=10007; int main(){ cin>>a>>b>>k>>n>>m; for(int i=1;i<=k;i++){ //利用杨辉三角求出二项式系数 c[i][0]=c[i][i]=1; for(int j=1;j<=i-1;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } int ans=c[k][m]; a%=mod;b%=mod; int aa=1,bb=1; for(int i=1;i<=n;i++) aa=aa*a%mod; for(int j=1;j<=m;j++) bb=bb*b%mod; ans=ans*aa%mod*bb%mod; cout<<ans<<endl; return 0; }
第二种方法:用逆元破解除法取模
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=1e6+10; const int INF=0x3fffffff; typedef long long LL; typedef unsigned long long ull; //可以用杨辉三角求出当a=1,b=1时xnym的系数,其实就是C(k,n)即C(n+m,n) 如果有a,b的话,很显然就是把C(n+m,n)乘上anbm //n+m=k a,b<10^6 int mod=10007; LL jc[maxn]; LL ksm(LL a,LL b){ //都记得要开LL LL res=1; while(b){ if(b&1){ res=res*a%mod; } b>>=1; a=a*a%mod; } return res%mod; } //求组合数的算法 LL c(LL n,LL m){ return jc[n]*(ksm(jc[m],mod-2)%mod)*(ksm(jc[n-m],mod-2)%mod); } int main(){ int a,b,k,n,m; jc[0]=jc[1]=1; scanf("%d %d %d %d %d",&a,&b,&k,&n,&m); for(int i=2;i<=k;i++){ jc[i]=jc[i-1]*i%mod; } LL res=1; res=ksm(a,n)*ksm(b,m)%mod*c(n+m,n)%mod; printf("%lld\n",res); return 0; }
1649:【例 2】2^k 进制数
其实根据样例分析一下也不难,主要是分类讨论,需要看最高位怎么处理,比如说样例里面,8进制的数字化为二进制至少不超过7位,那就说明了第七位只能取到1,也就是2^(w%k)-1,而数字从高位开始每一位又是递增的,所以要扣除小于首位的数字,那可以取得数字又少了。
2-len-1的第一类数:C(n,i)
len位的第二类数:for(int i=1;i<=c;i++) C(n-i,m) c=2^(w%k)-1
最后还要用高精度做组合数
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=282; const int INF=0x3fffffff; int total[maxn]; int gcd(int a,int b){ return b==0? a:gcd(b,a%b); } void cc(int n,int m){ //求C(n,m) if(n<m) return; int a[maxn],b[maxn]; //表示组合数的分子、分母 for(int i=m;i>=1;i--){ b[i]=i; //分母 要被全部化掉 a[i]=n-m+i; //分子 } for(int i=1;i<=m;i++){ if(b[i]==1) continue; //约分 去掉分母 for(int j=m;j>=1;j--){ int x=gcd(b[i],a[j]); a[j]/=x; b[i]/=x; if(b[i]==1) break; } } memset(b,0,sizeof(b)); //再拿来用 b[1]=1;b[0]=1; //把约分后的分子相乘 int jw=0; for(int j=1;j<=m;j++){ jw=0; if(a[j]==1) continue; for(int i=1;i<=b[0];i++){ b[i]=b[i]*a[j]+jw; jw=b[i]/10; b[i]%=10; if(i==b[0]&&jw) b[0]++; //进位 } } //累加到total数组 total[0]=max(total[0],b[0]); for(int i=1;i<=total[0];i++){ total[i]+=b[i]; total[i+1]+=total[i]/10; total[i]%=10; } if(total[total[0]+1]) total[0]++; } int main(){ int k,w,n,c,m; memset(total,0,sizeof(total)); cin>>k>>w; n=(1<<k)-1;m=w/k;c=w%k; for(int i=m;i>=2;i--) cc(n,i); c=(1<<c)-1; if(c>=1&&n>m){ //还可以填剩下的位数,并且后面的数有的选,满足数组不降 for(int i=1;i<=c;i++) cc(n-i,m); } for(int j=total[0];j>=1;j--) cout<<total[j]; return 0; }
1650:【例 3】组合
lucas定理模板题,记得开longlong
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; typedef long long ll; ll n,m,p; ll ksm(ll a,ll b){ ll s=1; while(b){ if(b&1) s=s*a%p; a=a*a%p; b>>=1; } return s; } ll c(ll n,ll m){ if(m>n) return 0; ll a=1,b=1; for(ll i=n-m+1;i<=n;i++) a=a*i%p; for(ll i=2;i<=m;i++) b=b*i%p; return a*ksm(b,p-2); //乘法逆元 } ll lucas(ll n,ll m){ if(!m) return 1; else return c(n%p,m%p)*lucas(n/p,m/p)%p; } int main(){ int t; cin>>t; while(t--){ cin>>n>>m>>p; cout<<lucas(n,m)<<endl; } return 0; }
1651:【例 4】古代猪文
这道题比较综合,会用到几个算法,首先题目不难推出结果式子 q^(sum(C(n,d)) mod q 其中d|n
首先q=999911659是个质数,q,n互质,可以由欧拉定理的推论推出 计算关键为 sum(C(n,d)) mod 9999116458 的结果
分解质因子:999911658=2*3*4679*35617,这样的数成为square-free-number ,所有质因子次数都为1
枚举n的约数d,用lucas定理计算出组合数,分别算出C(n,d)对2,3,4679,35617的模,记为a1,a2,a3,a4,然后用中国剩余定理求出线性同余方程组的解:
xmod 2=a1 xmod3=a2 xmod4679=a3 xmod35617=a4
(因为直接用lucas定理求组合数对999911658的模会超时)
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; typedef long long ll; const int a[4]={2,3,4679,35617}; int p[36000],b[4],mod=999911658,ans=0,n,g; int i,j,x,y; ll power(int a,int b){ ll c=1; while(b){ if(b&1) c=c*a%mod; a=(ll)a*a%mod; b>>=1; } return c; } void exgcd(int a,int b,int &x,int &y){ if(b==0){ x=1;y=0;return; } exgcd(b,a%b,x,y); int z=x; x=y; y=z-(a/b)*y; } int inv(int a,int p){ int x,y; exgcd(a,p,x,y); return (x%p+p)%p; } int calc(int x,int mod){ int ans=1,y,a,b; for(y=n;x;x/=mod,y/=mod){ a=x%mod;b=y%mod; //lucas定理 ans=(ll)ans*p[b]%mod*inv(p[a],mod)%mod*inv(b<a? 0:p[b-a],mod)%mod; //把递归转为了循环 } return ans; } int main(){ cin>>n>>g; g%=mod+1; if(g==0) { cout<<"0"<<endl;return 0; } for(p[0]=i=1;i<=a[3];i++) p[i]=(ll)p[i-1]*i%mod; //阶乘 for(i=1;i*i<=n;i++){ //枚举所有的因子 从1开始 if(n%i==0){ for(j=0;j<4;j++) b[j]=(b[j]+calc(i,a[j]))%a[j]; if(i*i!=n) for(j=0;j<4;j++) b[j]=(b[j]+calc(n/i,a[j]))%a[j]; //大的因子 } } for(i=0;i<4;i++) { //中国剩余定理 exgcd(mod/a[i],a[i],x,y); ans=(ans+(ll)x*(mod/a[i])%mod*b[i])%mod; } ans=(ans+mod)%mod; mod++; ans=power(g,ans); cout<<ans<<endl; return 0; }
标签:return,组合,int,ll,一本,数学,long,include,mod From: https://www.cnblogs.com/shirlybaby/p/17437034.html