2016 組合數問題
組合數問題
題面
對,就是這樣這樣的一道題,卡了博主好多天,emmmm,反正最後是看題解ac的……(是吧,我也感覺自己好弱)
首先,上一份自己看的題解,強烈推薦!!!
https://www.luogu.org/problemnew/solution/P2822
——————————————————————————————
然後,下面是我們的正文
我們是可以直接用公式暴力的,但是你可曾考慮到了資料範圍…………(博主只拿了20分)
這道題其實有很多細節,可以看出一丟丟思路(當然,一開始我也沒看出來,之後,打完程式碼之後再看了好幾遍題)
1.
在這個範圍中,可以看出 j 的範圍是肯定要小於 i 的,(這裡先不要考慮m的範圍
當i=0時,j=0;
當i=1時,j=0,1;
當i=2時,j=0,1,2;
……
這時候,我們可以看出這貌似是個三角形(我覺得思維遷徙能力比較強的,就應該已經想到了楊輝三角(因為組合數是和楊輝三角有關係的))
思維遷徙能力不好的,不要怕,因為博主就是……
那麼思考一下,為什麼上面博主說和楊輝三角有關係呢,這個時候就應該想到打表了吧,不知道為什麼有關係,幾個資料證實一下就可以了(用公式啊)自己實現
之後,我們就可以發現數據是這樣的1 11 121 1331 14641 …,這不就是楊輝三角!!!當發現這個的時候,有思路了吧,暴力啊,可是你在看一眼資料範圍……,(博主的暴力只拿了55分……)
之後再考慮其他的方法,在這裡引入一個新的概念“字首和
不懂的找度娘,這裡只說一個公式,s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
上加左減左上
然後看一下程式碼
#include<bits/stdc++.h> using namespace std; int c[2100][2100]; int s[2100][2100];//求字首和的陣列 int t,k; int n,m; void zhs() { c[0][0]=c[1][0]=c[1][1]=1; for(int j=2;j<=2010;j++) { c[j][0]=1; for(int l=1;l<=j;l++) { s[j][l]=s[j-1][l]+s[j][l-1]-s[j-1][l-1];//就是上面的公式 c[j][l]=(c[j-1][l-1]%k+c[j-1][l]%k)%k; if(c[j][l]%k==0) s[j][l]+=1; } s[j][j+1]=s[j][j];//敲黑板,劃重點,這個必須有!!詳見下面的解釋 } } int main() { //freopen("1.in","r",stdin); scanf("%d%d",&t,&k); zhs(); for(int i=1;i<=t;i++) { scanf("%d%d",&n,&m); if(m>n) cout<<s[n][n]<<endl; else cout<<s[n][m]<<endl; } return 0; }
注:s[j][j+1]=s[j][j];
假使沒有這一句,我們看一下輸出(根據第二個樣例)
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 3 0 3 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 4 0 6 0 4 0 1 0 0 0 0 0 0 0 0 0 0 0 0
0 1 1 5 2 10 3 10 4 5 *4 1 ** 0 0 0 0 0 0 0 0 0 0
0 1 1 6 3 15 5 20 7 15 7 6 3 1 0 0 0 0 0 0 0 0
0 1 1 7 3 21 6 35 9 35 9 21 **5 7 * 2 1 0 0 0 0 0 0
0 1 1 8 3 28 6 56 10 70 10 56 6 28 ***3 8 *** 1 1 0 0 0 0
0 1 1 9 3 36 6 84 10 126 10 126 6 84 3 36 1 9 0 1 0 0
0 1 2 10 5 45 9 120 14 210 14 252 11 210 9 120 8 45 8 10 8 1
只有黑體的地方就是s[j][j+1]的地方,但是,我們可以明顯的發現到不對,這個資料不對……怎麼辦??
這就是上面的解決的妙處了,傳遞,將 有下劃線的數字傳遞給黑體數字,這樣!!!!不就解決了,對吧