日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(lucas) Saving Beans

發布時間:2025/3/12 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (lucas) Saving Beans 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目:

Although winter is far away, squirrels have to work day and night to save beans. They need plenty of food to get through those long cold days. After some time the squirrel family thinks that they have to solve a problem. They suppose that they will save beans in n different trees. However, since the food is not sufficient nowadays, they will get no more than m beans. They want to know that how many ways there are to save no more than m beans (they are the same) in n trees.

Now they turn to you for help, you should give them the answer. The result may be extremely huge; you should output the result modulo p, because squirrels can’t recognize large numbers.
Input
The first line contains one integer T, means the number of cases.

Then followed T lines, each line contains three integers n, m, p, means that squirrels will save no more than m same beans in n different trees, 1 <= n, m <= 1000000000, 1 < p < 100000 and p is guaranteed to be a prime.
Output
You should output the answer modulo p.
Sample Input
2
1 2 5
2 1 5
Sample Output
3
3

Hint
For sample 1, squirrels will put no more than 2 beans in one tree. Since trees are different, we can label them as 1, 2 … and so on.
The 3 ways are: put no beans, put 1 bean in tree 1 and put 2 beans in tree 1. For sample 2, the 3 ways are:
put no beans, put 1 bean in tree 1 and put 1 bean in tree 2.

分析與解答

1.先說一下lucas
參考:https://blog.csdn.net/wyg1997/article/details/52152282
https://blog.csdn.net/wyg1997/article/details/52152282
lucas定理:

(圖片來自https://blog.csdn.net/arrowlll/article/details/53064748)
其實我們想一下二進制,111:就是1* 2^0+1*2^1+1*2^2
這個定理也是一樣的akak-1…..a1a0就是p進制的每一位
所以有如下解釋:

A、B是非負整數,p是質數。AB寫成p進制:A=a[n]a[n-1]…a[0],B=b[n]b[n-1]…b[0] 這里的每一個數組元素表示其p進制的每一位。
則組合數C(A,B)=C(a[n],b[n])C(a[n-1],b[n-1])…*C(a[0],b[0])。也就是說,把大組合數問題變成了一個個的小組合數。(A,B小于mod)

其實可以想一下快速冪,里面%2就是取二進制的最后一位數,/2就是二進制向前移動一位,所以怎么實現代碼也和快速冪類似,如下:

表達式:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p。(可以遞歸)
這里如果n/p和m/p很大,我們仍然可去調用lucas
遞歸方程:(C(n%p, m%p)*Lucas(n/p, m/p))%p。(遞歸出口為m==0,return 1)

LL Lucas(LL n,LL k) //Lucas定理遞歸 {if (k == 0) //遞歸終止條件 return 1;elsereturn C(n % mod , k % mod) * Lucas(n / mod , k / mod) % mod; }

對于每一個c(n,k)我們要寫一個函數也就是求(n! / ( k! * ( n - k)! ) )%mod,由于有除法取模,所以我們要求k!*(n - k)!的逆元。
這里用小費馬求逆元,我們還需寫一個快速冪。由于組合數里面有階乘,所以我們還需打一個階乘表

整體下來就是這個樣子
這里對mod取模

#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define CLR(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define LL long long LL mod = 1000003; LL fac[1000000+11] = {1,1}; void getfac() //打一個階乘表 {for (int i = 2 ; i <= 1000000 ; i++)fac[i] = fac[i-1] * i % mod; } LL quick_mod(LL n , LL m) //求快速冪 {LL ans = 1;n %= mod;while (m){if (m & 1)ans = ans * n % mod;n = n * n % mod;m >>= 1;}return ans; } LL C(LL n , LL k) //費馬小定理求逆元 {if (k > n)return 0;elsereturn fac[n] * (quick_mod(fac[k] * fac[n-k] % mod , mod - 2)) % mod; } LL Lucas(LL n,LL k) //Lucas定理遞歸 {if (k == 0) //遞歸終止條件 return 1;elsereturn C(n % mod , k % mod) * Lucas(n / mod , k / mod) % mod; } int main() {getfac();LL n,k;int Case = 1;int u;scanf ("%d",&u);while (u--){scanf ("%lld %lld",&n,&k);printf ("Case %d: %lld\n",Case++,Lucas(n,k));}return 0; }

2.再說一下這個題
m個小球放到n個盒子,盒子可以為空, 方案數就是:C(n+m-1,n-1)
這個題是m個果子放到n個樹上,類比一下,數就是盒子,小球就是果子,那么方案數就是C(n+m-1,n-1)
現在這個果子可以是0到m個,
所以總方案數
=C(n+m-1,n-1)+C(n+m-2,n-1)+…+C(n+1,n-1)+C(n,n-1)+C(n-1,n-1)
有一個組合數公式c(m,n)=c(m-1,n-1)+c(m-1,n)
這個組合數公式也可以看作:C(m,n-1)+C(m,n)=C(m+1,n)
還有個組合數公式:C(n,n)=1
所以我們從總方案數的后面變形
C(n-1,n-1)=C(n,n)
C(n-1,n-1)+C(n-1,n-1)=C(n,n)+C(n,n-1)=C(n+1,n)
現在后三個
C(n+1,n-1)+C(n,n-1)+C(n-1,n-1)=C(n+1,n-1)+C(n+1,n)=C(n+2,n)
我們發現,C(n+1,n-1)+C(n,n-1)+C(n-1,n-1)
這里m從2到0,上式等于C(n+2,n)
那么我們推廣到整個式子
C(n+m-1,n-1)+C(n+m-2,n-1)+…+C(n+1,n-1)+C(n,n-1)+C(n-1,n-1)
這里m從m到0,式子等于C(n+m,n)
推到這我們現在就只需求
C(n+m,m) % p

(我覺得我應該去學數學。。我愛數學啊)
現在這個題p不是固定的每次都會變,所以我們不能用打表了
C(n,m)=n!/(m!(n-m)!)
=(n*(n-1) … (n-m+1))/m!
分子從n-0一直到n-(m-1)也就是說循環m-1-0+1次也就是m次,而分母m!從1乘到m也是m次,那么我們就可以每次通過一個循環計算出C(n,m)了

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; }return up*Pow(down,p-2,p)%p; }

參考:
https://blog.csdn.net/qq_27717967/article/details/51493877
這個是wrong answer 我也不知道為什么

//當取模的p小于1e9時,不用預處理 #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; typedef long long ll; //計算n的k次方對M取模,二分法 ll Pow(ll n, ll k, ll p){ll ans=1;while(k){if(k&1)ans=(ans*n)%p;n = (n*n)%p;k>>=1; //k=k>>1 k=k/2;}return ans; } 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; }return up*Pow(down,p-2,p)%p; } ll lucas(ll n,ll m,ll p){if(m==0) return 1;return C(n%p,m%p,p)*lucas(n/p,m/p,p); } int main(){int t=1;ll m,n,p;scanf("%d",&t);while(t--){scanf("%I64d%I64d%I64d",&n,&m,&p);printf("%I64d\n",lucas(n+m,m,p));}return 0; }

后來改成預處理,lucas也改變,才對。
但是這個代碼改到上一個代碼的那個題上,就runtime error
我也不知道為什么,玄學就玄學到這了

#include <iostream> #include <string.h> #include <cmath> #define ll long long using namespace std; const int maxn=100002; ll n,m,p; ll fac[maxn];void getfac(ll p)//預處理階層 {fac[0]=1;for(int i=1;i<=p;i++)fac[i]=fac[i-1]*i%p; }ll power(ll a,ll n,ll p)//快速冪運算 {ll ans=1;while(n){if(n&1)ans=ans*a%p;a=a*a%p;n/=2;}return ans; }ll lucas(ll n,ll m,ll p) {ll ans=1;while(n&&m){ll a=n%p;ll b=m%p;if(a<b) return 0;ans=(ans*fac[a]*power(fac[b]*fac[a-b]%p,p-2,p))%p;// fac[b]*fac[a-b]后面別忘了%p,否則WAn/=p;m/=p;}return ans; }int main() {int t;cin>>t;while(t--){cin>>n>>m>>p;getfac(p);cout<<lucas(n+m,m,p)<<endl;}return 0; }

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

1 <= n, m <= 1000000000, 1 < p < 100000 and p is guaranteed to be a prime.第二個代碼對

總結

以上是生活随笔為你收集整理的(lucas) Saving Beans的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。