[Noip2017]組合數問題
阿新 • • 發佈:2020-08-09
[Noip2017]組合數問題
一.前言
組合數學沒學好阿巴阿巴……題目連結
二.思路
首先給一個結論:楊輝三角和組合數的關係是密不可分的。這兩個就是一個東西其實。先來康康一個很顯而易見的數學公式。
\[C_m^n=C_{m}^{n-1}+C_{m-1}^{n-1} \]
大概,還是很容易理解的,吧……稍作解釋就是。
在 n 個數中選 m 個做組合的情況可以從 在前 n-1 個數中就選出 m 個和 在前 n-1個數中選出 m-1 個轉移過來(後一種是第 n 個塞進去)
然後稍加觀察,若是把 n 當作行, m 當作列的話,就是楊輝三角。換而言之,\(C^i_j\) 為楊輝三角的第 i 行,第 j 列的值。
然後我們再看問題,求的是 k 的倍數的個數,也就是 \(\%k=0\) 的個數,這裡將詢問離線,預處理直接\(O(1)\)解決詢問。所以預處理一個基於楊輝三角的字首和陣列 \(sum[n][m]\) 表示答案。轉移方程為
\[sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1] \]
然後加一個當前值是不是 \(\%k=0\) 判斷就ok。
關於轉移方程,可以具體手推,這裡給出一個抽象理解。(今天手比較抖)
框住的表示這一部分的字首和,現在是在求紅框。灰色是重複部分。
但是這是常規的楊輝三角,計算機裡面的楊輝三角,稍微有點點不同,是個方的,所以畫圖為
大概是這種感覺,然後可以發現籃框右下角是空點,實際上空點的值應該是空點左邊的值,為了方便計算就有一個\(sum[i][i+1]=sum[i][i]\),後面推就沒有問題了。
三.CODE
#include<iostream> #include<cstdio> #include<algorithm> #include<fstream> #include<cmath> using namespace std; int read(){ char ch=getchar(); int res=0,f=1; for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0'); return res*f; } int t,k,n,m; int f[2005][2005],sum[2005][2005]; int main(){ t=read();k=read(); for(int i=0;i<=2004;++i)f[i][i]=f[i][0]=1; for(int i=1;i<=2004;++i){ for(int j=1;j<=i;++j){ f[i][j]=(f[i-1][j]+f[i-1][j-1])%k;//一直膜就對了 sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]); if(!f[i][j])sum[i][j]++; } sum[i][i+1]=sum[i][i];//比較關鍵的一步 } while(t--){ n=read();m=read(); cout<<sum[n][min(n,m)]<<endl; } return 0; }