如何求大组合数(模)(ACM算法)
意思比较简单。由m * * * 0s和n 1组成,但从左到右,1出现的次数并不比0少。
通过丹尼尔的算法,结果是C(m+n,n)-C(m+n,m-1),然后我们可以简化公式:
(n+m)!*
(n-m+1) / ((m)!* (n+1)!)
再拿模块来说,但是因为组合数量多,直接用大数做乘除会超时。看了别人的报告,知道可以用质数n快速化简模块!= 2^p[i] *
3便士[1]* 5便士[1]*............................................................................................................................................................。。
# include & ltiostream & gt
# include & ltstdio.h & gt
# include & ltstring.h & gt
使用命名空间std
#定义M 2000005
#定义mm 20100501
布尔符号[M];
int prime[150000],p[150000],len//质数记录质数,p记录质数的幂,len记录长度。
Void getprime() // sieve方法查找质数。
{
int i,j,k = 0;
prime[k++]= 2;
for(I = 3;我& lt= M;i+=2)
{
如果(!签名[i])
{
prime[k++]= I;
for(j = I;j & lt= M;j+=i)
SIG[j]= 1;
}
}
}
void get(int k,int s) // K!的素数分解是指数(分母,分子)的加法和减法
{
int i,mid
for(I = 0;prime[I]& lt;= k & amp& amp素数[I];i++)
{
mid = k;
while(mid)
{
如果(s)
p[I]+= mid/prime[I];
其他
p[I]-= mid/prime[I];
mid/= prime[I];
}
}
if(len & lt;我)
len = I;
}
__int64 cal() //计算结果(prime [I...] p [I...])% mm
{
__int64 i,ans = 1;
for(I = 0;我& lt= leni++)
{
如果(p[i])
{
__int64 t = prime[i],b = p[i],ret = 1;
While(b) //计算(t^b)% mm
{
if(b % 2)ret * = t % mm;
t = t * t % mm
b/= 2;
}
ans =(ans * ret)% mm;
}
}
返回ans
}
int main()
{
int t,m,n,I,mid
_ _ int64 ans
getprime();
CIN & gt;& gtt;
while(t -)
{
CIN & gt;& gtn & gt& gtm;
len = 0;
memset(p,0,sizeof(p));
mid = n-m+1;//之前要把n-m+1的因子加到P上才能做出(m+n)!/ ((m)!*(n+1)!)可分
for(I = 0;mid & gt1;i++)
{
if( mid%prime[i] == 0)
{
while(mid%prime[i]==0)
{
p[I]+= 1;
mid/= prime[I];
}
}
}
get(m+n,1);
get(m,0);
get(n+1,0);
ans = cal();
printf("%I64d\n ",ans);
}
返回0;
}
你可以用质数分解法,
先找出上下的质数,然后除以,再用幂公式。