如何求大组合数(模)(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;

}

你可以用质数分解法,

先找出上下的质数,然后除以,再用幂公式。