1. 程式人生 > >Lucas定理(求組合數,例題FZU2020,HDU3944)

Lucas定理(求組合數,例題FZU2020,HDU3944)

Lucas定理:用於求C(n,m) mod p,其中p為素數

證明等在網上都可以找到,我也不是很懂就略過了(懂了補上)。

直接貼出用法吧:

主要程式碼就兩行,需要用到的知識有快速冪求逆元(計算組合數),必要的時候需要打表(計算階乘)

核心程式碼:
 

ll lucas(ll a,ll b,ll p)
{
	if (b==0) return 1;
    //C是求組合數的函式
	return C(a%p,b%p,p)*lucas(a/p,b/p,p);
}

具體應用(關鍵:求組合數時要用到快速冪和求逆元)

例題1(不用打表的):

 Problem 2020 組合(FZU2020)

Accept: 1614    Submit: 3822
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

給出組合數C(n,m), 表示從n個元素中選出m個元素的方案數。例如C(5,2) = 10, C(4,2) = 6.可是當n,m比較大的時候,C(n,m)很大!於是xiaobo希望你輸出 C(n,m) mod p的值!

 Input

輸入資料第一行是一個正整數T,表示資料組數 (T <= 100) 接下來是T組資料,每組資料有3個正整數 n, m, p (1 <= m <= n <= 10^9, m <= 10^4, m < p < 10^9, p是素數)

 Output

對於每組資料,輸出一個正整數,表示C(n,m) mod p的結果。

 Sample Input

2 5 2 3 5 2 61

 Sample Output

1 10

 Source

題意:很簡單就直接用Lucas定理就行,也不會超時

#include <cstdio>
#include <iostream>
typedef long long ll;
using namespace std;

//快速冪
ll pow_mod(ll a,ll x,ll p)
{
	ll ret=1;
	while(x)
	{
		if (x&1) ret=ret*a%p;
		a=a*a%p;
		x>>=1;
	}
	return ret;
}

//求組合數
ll C(ll n,ll m,ll p)
{
	if (m==0) return 1;
	if (m>n-m) m=n-m;
	ll up=1,down=1;
    //求分子分母
	for (int i=1;i<=m;i++)
	{
		up=(up*(n-i+1))%p;
		down=(down*i)%p;
	}
    //利用快速冪求逆元,其中pow_mod(down,p-2,p)是逆元
	return up*pow_mod(down,p-2,p)%p;
}

//lucas定理
ll lucas(ll a,ll b,ll p)
{
	if (b==0) return 1;
	return C(a%p,b%p,p)*lucas(a/p,b/p,p);
}

int main()
{
	ll n,m,p,T;
	cin >> T;
	while(T--)
	{
		cin >> n >> m >> p;
		cout << lucas(n,m,p) << endl;
	}
	return 0;
}

例題2(需要打表):

DP?(HDU3944)

Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 128000/128000 K (Java/Others)
Total Submission(s): 3897    Accepted Submission(s): 1247


 

Problem Description


Figure 1 shows the Yang Hui Triangle. We number the row from top to bottom 0,1,2,…and the column from left to right 0,1,2,….If using C(n,k) represents the number of row n, column k. The Yang Hui Triangle has a regular pattern as follows.
C(n,0)=C(n,n)=1 (n ≥ 0) 
C(n,k)=C(n-1,k-1)+C(n-1,k) (0<k<n)
Write a program that calculates the minimum sum of numbers passed on a route that starts at the top and ends at row n, column k. Each step can go either straight down or diagonally down to the right like figure 2.
As the answer may be very large, you only need to output the answer mod p which is a prime.

Input

Input to the problem will consists of series of up to 100000 data sets. For each data there is a line contains three integers n, k(0<=k<=n<10^9) p(p<10^4 and p is a prime) . Input is terminated by end-of-file.

Output

For every test case, you should output "Case #C: " first, where C indicates the case number and starts at 1.Then output the minimum sum mod p.

Sample Input

1 1 2 4 2 7

Sample Output

Case #1: 0 Case #2: 5

Author

[email protected]

Source

題意:給你一個楊輝三角,讓你求弄頂部到某一位置的最短路徑(只能向下和右下走)。大膽猜想可以一直向下然後一直想右下走,或者一直向右下然後一直向下,此時路徑最短。這兩種情況可以綜合來考慮,考慮到資料範圍猜想可能可以化簡公式,經隊友提示隊友得到公式為C(n+1,k)+n-k,對於k+1>n/2時轉換成向下走的情況即,k=n-k。

注意:若不打表直接計算組合數可能會超時,由於p較小,可以預處理所以p的情況,進行打表。

#include <cstdio>
#include <iostream>
#include <cstring>
#define MAX_N 10005
typedef long long ll;
using namespace std;

int cnt=0,sum=0;
ll n,p,k;
ll c[MAX_N][MAX_N];
ll prime[MAX_N],flag[MAX_N];
ll pos[MAX_N];

//快速冪
ll pow_mod(ll a,ll x,ll p)
{
	ll ret=1;
	while(x)
	{
		if (x&1) ret=ret*a%p;
		a=a*a%p;
		x>>=1;
	}
	return ret;
}

//打表
void init()
{
	memset(flag,0,sizeof(flag));
    //求出所有素數存入prime,用pos記錄位置
	for (int i=2;i<=10000;i++)
	{
		if (!flag[i])
		{
			prime[sum]=i;
			//printf("%lld\n",prime[sum]);
			pos[i]=sum++;
		}
		for (int j=i;j<=10000;j+=i)
			flag[j]=1;
	}
    //計算對於p是i的階乘
	for (int i=0;i<sum;i++)
	{
		c[i][0]=1; c[i][1]=1;
		for (int j=2;j<=prime[i];j++)
			c[i][j]=(c[i][j-1]*j)%prime[i];
	}
}

//求組合數
ll C(ll n,ll m,ll p)
{
	if (n<m) return 0;
    //利用逆元已經已經求得的階乘計算組合數
	return c[pos[p]][n]*pow_mod(c[pos[p]][m]*c[pos[p]][n-m],p-2,p)%p;
}

//還是兩行呼叫lucas
ll lucas(ll a,ll b,ll p)
{
	if (b==0) return 1;
	else return C(a%p,b%p,p)*lucas(a/p,b/p,p);
}

int main()
{
	init();
	while(~scanf("%lld%lld%lld",&n,&k,&p))
	{
        //將兩種情況合併為一種的操作,注意不可少
		if (k+1>n/2) k=n-k;
		printf("Case #%d: %lld\n",++cnt,(lucas(n+1,k,p)+n-k)%p);
	}
	return 0;
}

相關推薦

Lucas定理合數例題FZU2020HDU3944

Lucas定理:用於求C(n,m) mod p,其中p為素數 證明等在網上都可以找到,我也不是很懂就略過了(懂了補上)。 直接貼出用法吧: 主要程式碼就兩行,需要用到的知識有快速冪和求逆元(計算組合數),必要的時候需要打表(計算階乘) 核心程式碼:   ll l

[2011山東ACM省賽] Binomial Coeffcients合數

取余 cor memory -s sin mage pad ruby end Binomial Coeffcients Time Limit: 1000ms Memory limit: 65536K 有疑問?點這裏^_^ 題目描寫敘述 輸入 輸

Lucas定理與大合數的取模的求法總結

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

[演算法 18_001] Lucas 定理與大合數取餘

Lucas 定理 該定理是用來求當 (nm) ( n m

POJ 2992 Divisors合數因子個數

Divisors Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12048 Accepted: 3594 Descr

合數取模楊輝三角打表 & 逆元擴充套件歐幾里得、費馬小定理、尤拉定理、線性求法 & Lucas

    在acm競賽中,組合數取模的題目還是經常會見到的,所以這是有必要掌握的一個演算法。我本人就因為這個東西而被坑了很多次了= =之前的部落格也都扯過了,就不多說了,下面進入正題。 (1)楊輝三角求組合數     楊輝三角這個東西應該都不陌生,三角的兩邊始終為一,之後向

Lucas定理合數模板

end code == turn tdi div rac bsp 模板 $Lucas(n,m,p)=C(n\%p,m\%p)*Lucas(n/p,m/p,p)$ $C^n_m=\frac{n!}{m!(n-m)!}$ $x^{p-1}\equiv 1(mod p)\Long

hdu 3037 費馬小定理+逆元合數+Lucas定理

void log 打表 數學 mod turn ret iostream toc 組合數學推推推最後,推得要求C(n+m,m)%p 其中n,m小於10^9,p小於1^5 用Lucas定理求(Lucas定理求nm較大時的組合數) 因為p數據較小可以直接階乘打表求逆元

【POJ - 1942 】Paths on a Grid 組合數學合數的無數種方法

題幹: Imagine you are attending your math lesson at school. Once again, you are bored because your teacher tells things that you already mastered ye

合數的遞迴實現Cnm

此法借鑑了2009年華為一筆試題我寫的一個遞迴演算法http://blog.csdn.net/challenge_c_plusplus/article/details/6640530排列數的遞迴實現見我的另一篇http://blog.csdn.net/challenge_c_

Lucas定理(合數取模) 擴充套件Lucas定理(解決模數非質情況)

在比賽時 , 如果遇到CmnCnm的n比較大 , 我們不能通過預處理階乘和逆元來計算 , 而題目又要求對答案取一個質數模的時候 , 我們可以用Lucas定理來簡化計算 Lucas 定理: 定義 : n,m是非負整數,p是素數時 , Lucas(

c語言實現合數帶點優化的思想防止溢位

這是大家都知道的組合數,思想也很簡單,但是裡面的階乘,容易溢位,讓m!/n!先約分,減小數的大小,m!/n! = (n+1)(n+2)(n+3)···(m-1)(m); 如果m-n > n的話,我們就讓n = m-n.j儘可能讓乘起來的數小一點。程式碼列印的是25裡

Lucas定理合數

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm>

51Nod 1119 機器人走方格 擴充套件歐幾里得+逆元+合數

M * N的方格,一個機器人從左上走到右下,只能向右或向下走。有多少種不同的走法?由於方法數量可能很大,只需要輸出Mod 10^9 + 7的結果。 收起 輸入 第1行,2個數M,N,中間用空格隔開。(2 <= m,n <= 1000000) 輸出 輸出走法的數量 Mo

poj 3252 Round Numbers 楊輝三角合數

題目連結:poj 3252 題意:給出範圍為 [a , b] 的區間,問在這區間內的每個數字,假如它的二進位制位0的個數大於1的個數,就說明它是Round Numbers,問你有多少個Round Numbers數? 題解:首先楊輝三角求組合數學,見程式碼。   ///此

ZZULIOJ.1100: 合數函式專題

1100: 求組合數(函式專題) 題目描述 馬上要舉辦新生程式設計競賽了,與以往不同的是,本次比賽以班為單位,為了全面衡量一個班級的整體水平,要求從一個班的m位同學中任選k位同學代表本班參加比賽,問有多少種組合方案。顯然,這個組合數是m!/(k!(m-k)!)。要求編寫函式fact(

ZZULIOJ 1100: 合數函式專題

題目描述 馬上要舉辦新生程式設計競賽了,與以往不同的是,本次比賽以班為單位,為了全面衡量一個班級的整體水平,要求從一個班的m位同學中任選k位同學代表本班參加比賽,問有多少種組合方案。顯然,這個組合數是m!/(k!(m-k)!)。要求編寫函式fact(),實現求一個數的階乘功能,在主函式中呼叫

PTA-合數C語言

#include<stdio.h> double fact(int num){ double result=num; for(int i=num-1;i>0;i--){ result*=i; } return result; } int main(){

合數完善中.......

1.楊輝三角遞推法 void init_trangle() { for(int i = 0; i < 500; i ++) { cc[i][0] = cc[i][i] = 1; for(int j = 1; j < i; j++) { cc[i][j] =(cc

hdu6333 合數加莫隊n個蘋果最多取m個方案數。

題解: 令表示n個蘋果最多取m個的方案數,很容易想到 根據楊輝三角也很容易推出 我們將m-n當作一條線段,那麼就是這條線段的函式值,而根據上面的兩個公式,又可以在O(1)的時間內實現到、、、的轉移。利用莫隊演算法離線處理即可。 程式碼: #include<bits/