コンビネーション数の詳細
66102 ワード
概念:組合せ数はC(n,m)で表し,n個の数の中でm個の数を取るスキームを表す.(この概念は主に問題を組合せ数に抽象化するために用いられる).式:組合せ数の式も多くなく、1、C(n,m)=C(n,n-m).2、C(n,m)=C(n-1,m-1)+C(n-1,m).これはとても重要で、これは楊輝三角の繰返し式と同じなので、私たちはよく楊輝三角と組み合わせ数を合わせて見ます.典題3,C(0,n)+C(1,n)+C(2,n)+C(3,n)+...C(n,n)=2^nという式は二次項の定理として用いられ,これもよく用いられる.この上の3つは私たちがよく使うものです(大物を選んで左に曲がります).求法:組合せ数に対する求法が多い:Lucas定理、繰返し、逆元.プッシュ:これは実は楊輝三角をプッシュしています.これは主にnとm<=2000で、多くの場合に使われています.栗を挙げると、このような問題に対して私たちはプッシュに適しています.適用範囲:n<=2000(nは組合せ数の下付きの値、すなわちC(n,m)中のn)である.ボードのサブコード:
タイトルコード:
階乗逆元:この方法は組合せ数の定義式に基づいて求め,C(n,m)=n!/(m!*(n-m)!),だから私たちはすべての階乗と逆元を前処理します.まず、逆元の前処理を乗算します.逆元を乗算しない点この適用範囲:nmodの場合、modの倍数で逆元がない場合があります.これは間違っています.
ボードコード(拡張ユークリッド):
Lucas定理:この定理は個人的にはまあまあだと思いますが、主にmodに対しては小さいので、階乗を使うとmodの倍数になる可能性があります.このように逆元で組み合わせ数を求めるとggになります.しかし、プッシュがまた詰まった場合、私たちはこれしか使えません.適用範囲:繰返しと階乗逆元が掛けられている場合、これを使用します.式:Lucas(n,m,mod)(nとmはC(n,m)のうち、modは型取り対象)=C(n%mod,m%mod)*Lucas(n/mod,m/mod,mod).コード:このコードは2種類あります.公式には組み合わせ数がありますが、範囲が下がっただけなので、Lucasの定理で元のカード死の繰返しと階乗の逆元を救い返します.だから私たちは2つのコードを持っています.modが比較的小さい場合,繰返しを用いることができ,modが比較的大きい場合,次乗逆元を用いることができる.プッシュ:
階乗逆元:これには小さな最適化がありますが、modは質量数でなければなりません.そうしないと、mod以内の階乗がmodの倍数ではないことを保証します.フェルマの定理:
ユークリッドを開拓:
补充:この组み合わせ数にはもう一つの超牛折りの原理があります:仕切り板の原理.これは何ですか.実は難しくないです.質問原型:n個の品物をあげます.これらの品物は一毛のように、m-1個の仕切りでmグループに分けます(各グループには品物があります).何種類のプランがありますか.これは組み合わせ数と何の関係がありますか?もちろん関係あります!別の方法で考えてみましょう.このm-1個の仕切り板については、実際にはn-1個の位置を置くことができますが、各方法については1つの案に適切に対応しているので、この問題はm-1個の仕切り板について、n-1個の位置を置くことができます.置く案の数を聞くことができますか.これが組み合わせ数ではないでしょうか.この問題にはもう一つの変形があり、私たちが要求しているのはグループごとに品物があることに気づいたが、もし品物がなければいいのだろうか.実は難しくないです.私たちはこのように考えることができて、すべてのグループのmに対して、私たちはすべて人為的にm個の品物をプラスして、このように上の問題ではありませんか?n+m-1箇所にm-1個の仕切り板を置く.以上がグループ数向上グループのすべての知識であり、皆さんが見て理解してほしい.不明な場合はメッセージでお問い合わせください.
#include
#include
using namespace std;
int C[1000][1000],mod=1e9+9;
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=0;i<=n;i++)//
{
C[i][1]=i%mod;// i i
C[i][i]=C[i][0]=1;// i i
}
for(int i=2;i<=n;i++)//
for(int j=2;j<i;j++)//
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;// ,
printf("%d",C[n][m]);//
return 0;
}
タイトルコード:
#include
#include
using namespace std;
long long C[2010][2010],sum[2010][2010],mod;
void pre()
{
for(int i=0;i<=2000;i++)
{
C[i][1]=i%mod;
C[i][i]=C[i][0]=1;
}
for(int i=2;i<=2000;i++)
for(int j=2;j<i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
for(int i=1;i<=2000;i++)
for(int j=1;j<=i;j++)// i 1 j mod
if(C[i][j]==0)// mod , 0 mod
sum[i][j]=sum[i][j-1]+1;//
else sum[i][j]=sum[i][j-1];//
return ;
}
int main()
{
int n,m,T;
scanf("%d %d",&T,&mod);//
pre();// 2000
while(T--)
{
long long ans=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
if(i>m)// , i
ans+=sum[i][m];
else ans+=sum[i][i];
printf("%lld
",ans);//
}
return 0;
}
階乗逆元:この方法は組合せ数の定義式に基づいて求め,C(n,m)=n!/(m!*(n-m)!),だから私たちはすべての階乗と逆元を前処理します.まず、逆元の前処理を乗算します.逆元を乗算しない点この適用範囲:nmodの場合、modの倍数で逆元がない場合があります.これは間違っています.
#include
#include
using namespace std;
long long inv[1000100],jc[1000100],mod=1e9+9;
long long pow(long long a,long long b)//
{
long long ans=1;
while(b)
{
if(b%2==1)
ans=(ans*a)%mod;
a=(a*a)%mod;
b=b>>1;
}
return ans;
}
void pre()
{
jc[1]=1;//
for(int i=2;i<=1000000;i++)
jc[i]=(jc[i-1]*i)%mod;//
inv[1000000]=pow(jc[1000000],mod-2);//
for(int i=999999;i>=0;i--)
inv[i]=(inv[i+1]*(i+1))%mod;//
return ;
}
int main()
{
pre();
int T;
scanf("%d",&T);
while(T--)
{
long long n,m;
scanf("%lld %lld",&n,&m);
printf("%lld
",(((jc[n]*inv[m])%mod)*inv[n-m])%mod);// , mod, mod, mod!
}
}
ボードコード(拡張ユークリッド):
#include
#include
using namespace std;
int mod=1e9+9,inv[1000100],jc[1000100],x,y;
void gcd(int a,int b)//
{
if(b==0)
{
x=1;
y=0;
return ;
}
gcd(b,a%b);
int k=x;
x=y;
y=k-a/b*y;
return ;
}
void pre()
{
jc[1]=1;
for(int i=2;i<=1000000;i++)//
jc[i]=(jc[i-1]*i)%mod;
gcd(jc[1000000],mod);//
inv[1000000]=(x+mod)%mod;//
for(int i=999999;i>=0;i--)//
inv[i]=(inv[i+1]*(i+1))%mod;
return ;
}
int C(int n,int m)
{
return (((1LL*jc[n]*inv[m])%mod)*inv[n-m])%mod;//
}
int main()
{
int T;
scanf("%d",&T);
pre();
while(T--)
{
int n,m;
scanf("%d %d",&n,&m);//
printf("%d
",C(n,m));//
}
return 0;
}
Lucas定理:この定理は個人的にはまあまあだと思いますが、主にmodに対しては小さいので、階乗を使うとmodの倍数になる可能性があります.このように逆元で組み合わせ数を求めるとggになります.しかし、プッシュがまた詰まった場合、私たちはこれしか使えません.適用範囲:繰返しと階乗逆元が掛けられている場合、これを使用します.式:Lucas(n,m,mod)(nとmはC(n,m)のうち、modは型取り対象)=C(n%mod,m%mod)*Lucas(n/mod,m/mod,mod).コード:このコードは2種類あります.公式には組み合わせ数がありますが、範囲が下がっただけなので、Lucasの定理で元のカード死の繰返しと階乗の逆元を救い返します.だから私たちは2つのコードを持っています.modが比較的小さい場合,繰返しを用いることができ,modが比較的大きい場合,次乗逆元を用いることができる.プッシュ:
#include
#include
using namespace std;
int C[2010][2010],mod;
void pre()//
{
for(int i=1;i<=mod;i++)
{
C[i][1]=i%mod;
C[i][i]=C[i][0]=1;
}
for(int i=2;i<=mod;i++)
for(int j=2;j<i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
return ;
}
int Lucas(int n,int m)
{
if(m==0)//
return 1;
return (C[n%mod][m%mod]*Lucas(n/mod,m/mod))%mod;//Lucas
}
int main()
{
int T;
scanf("%d %d",&T,&mod);
pre();
while(T--)
{
int n,m;
scanf("%d %d",&n,&m);
printf("%d
",Lucas(n,m));//Lucas
}
return 0;
}
階乗逆元:これには小さな最適化がありますが、modは質量数でなければなりません.そうしないと、mod以内の階乗がmodの倍数ではないことを保証します.フェルマの定理:
#include
#include
using namespace std;
int jc[1000100],inv[1000100],mod;
int pow(long long a,long long b)//
{
long long ans=1;
while(b)
{
if(b%2==1)
ans=(ans*a)%mod;
a=(a*a)%mod;
b=b>>1;
}
return (int)ans;
}
void pre()//
{
jc[1]=1;
for(int i=2;i<mod;i++)
jc[i]=(jc[i-1]*i)%mod;
inv[mod-1]=pow(jc[mod-1],mod-2);//
for(int i=mod-2;i>=0;i--)
inv[i]=(inv[i+1]*(i+1))%mod;
}
int C(int n,int m)//
{
return (((jc[n]*inv[m])%mod)*inv[n-m])%mod;
}
int Lucas(int n,int m)
{
if(m==0)//
return 1;
return (C(n%mod,m%mod)*Lucas(n/mod,m/mod))%mod;//Lucas
}
int main()
{
int T;
scanf("%d %d",&T,&mod);
pre();
while(T--)
{
int n,m;
scanf("%d %d",&n,&m);
printf("%d
",Lucas(n,m));//
}
return 0;
}
ユークリッドを開拓:
#include
#include
using namespace std;
int mod,inv[1000100],jc[1000100],x,y;
void gcd(int a,int b)//
{
if(b==0)
{
x=1;
y=0;
return ;
}
gcd(b,a%b);
int k=x;
x=y;
y=k-a/b*y;
return ;
}
void pre()//
{
jc[1]=1;
for(int i=2;i<mod;i++)
jc[i]=(jc[i-1]*i)%mod;
gcd(jc[mod-1],mod);//
inv[mod-1]=(x+mod)%mod;//
for(int i=mod-2;i>=0;i--)
inv[i]=(inv[i+1]*(i+1))%mod;
return ;
}
int C(int n,int m)//
{
return (((1LL*jc[n]*inv[m])%mod)*inv[n-m])%mod;
}
int Lucas(int n,int m)
{
if(m==0)
return 1;
return (1LL*C(n%mod,m%mod)*Lucas(n/mod,m/mod))%mod;
}
int main()
{
int T;
scanf("%d %d",&T,&mod);// ,mod
pre();
while(T--)
{
int n,m;
scanf("%d %d",&n,&m);
printf("%d
",Lucas(n,m));//
}
return 0;
}
补充:この组み合わせ数にはもう一つの超牛折りの原理があります:仕切り板の原理.これは何ですか.実は難しくないです.質問原型:n個の品物をあげます.これらの品物は一毛のように、m-1個の仕切りでmグループに分けます(各グループには品物があります).何種類のプランがありますか.これは組み合わせ数と何の関係がありますか?もちろん関係あります!別の方法で考えてみましょう.このm-1個の仕切り板については、実際にはn-1個の位置を置くことができますが、各方法については1つの案に適切に対応しているので、この問題はm-1個の仕切り板について、n-1個の位置を置くことができます.置く案の数を聞くことができますか.これが組み合わせ数ではないでしょうか.この問題にはもう一つの変形があり、私たちが要求しているのはグループごとに品物があることに気づいたが、もし品物がなければいいのだろうか.実は難しくないです.私たちはこのように考えることができて、すべてのグループのmに対して、私たちはすべて人為的にm個の品物をプラスして、このように上の問題ではありませんか?n+m-1箇所にm-1個の仕切り板を置く.以上がグループ数向上グループのすべての知識であり、皆さんが見て理解してほしい.不明な場合はメッセージでお問い合わせください.