在计算A(n,m) 或 C(n,m) 时,如果n和m特别大的话,一般题目会要求你得到这个结果取模于一个特别大的质数后的结果,暴力求解对于计算C(n,m)是一件困难的事,而且如果题目要求计算大量的排列数和组合数的话,暴力法特别慢。
下面分享一种快速计算排列数和组合数的方法,要求取模的数必须大,而且是质数,该方法在进行预处理过后,得到每一个排列数和组合数的时间复杂度为 O(1) 。废话不多说,直接给出代码,当模板记下。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353,N=1e7+5;
int n,a[N],b[N];
int A(int n,int m)
{
return a[n]*b[n-m]%mod;
}
int C(int n,int m)
{
return a[n]*b[n-m]%mod*b[m]%mod;
}
int qsm(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void QWQ(int n)
{
a[0]=1;
for(int i=1;i<=n;i++) a[i]=a[i-1]*i%mod;
b[n]=qsm(a[n],mod-2);
for(int i=n-1;i>=0;i--) b[i]=b[i+1]*(i+1)%mod;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
QWQ(1e7);// 预处理
int x,y;
cin>>x>>y;
cout<<A(x,y)<<endl<<C(x,y);
return 0;
}
下面给出一个例题,是icpc真题^_^
原题链接:Alice and Bob - QOJ 6736 - Virtual Judge
题解:
题意:如果p1=k,那么可以对区间[1,k]进行任意顺序重排列,谁先用两次相同的p1,谁就输。
对于样例10:
(1)p1=1 ==> 只可以对区间[1,1]进行重排,分析可知先手必输共有A(9,9)种。
(2)p1=2 ==> 可以对区间[1,2]进行操作,假设区间[1,2]为2 1那么Alice的最优操作是将1移动到第一位,那么Bob输,否则无论Alice如何操作,都会将一个大于等于2的数移动到第一位,那么Bob只需再将2移动到第一位,就会导致Alice使用2次2,导致Alice输,即Bob赢,共有C(8,1)*A(8,8)。
(3)同理,如果p1=k,如果区间[1,k]中存在比k小的,那么最优操作都是将比k小的移动到第一位,并且将k移动到第k位上,因为这样就能够保证下一个人无法再将k移动到第一位,导致‘我’使用两次k。通过分析最后还是Bob输。相对的,如果区间[1,k]都是大于等于k的数,那么Bob就会赢。
分析Bob赢的种数:k=3 ==> C(7,1)*C(6,1)*A(7,7)
k=4 ==>C(6,1)*C(5,1)*C(4,1)*A(6,6) k=5 ==>C(5,1)*C(4,1)*C(3,1)*C(2,1)*A(5,5)
k=6,7,8,9,10 ==>0
综上所述,答案为for (int i = n / 2; i <= n - 1; i++) ans += A( n - i - 1,i) * A(i,i);
题解代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353,N=1e7+5;
int n,a[N],b[N];
int A(int n,int m)
{
return a[n]*b[n-m]%mod;
}
int qsm(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
a[0]=1;
for(int i=1;i<=n;i++) a[i]=a[i-1]*i%mod;
b[n]=qsm(a[n],mod-2);
for(int i=n-1;i>=0;i--) b[i]=b[i+1]*(i+1)%mod;
int ans=0;
for(int i=n/2;i<=n-1;i++)
ans=(ans+A(i,n-i-1)*a[i])%mod;
cout<<ans;
return 0;
}