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;
}
posted on
浙公网安备 33010602011771号