noip2016 組合數問題
阿新 • • 發佈:2017-07-27
意義 cstring ans 數據 using 組合數 https 技術分享 cdn
題目描述
組合數表示的是從n個物品中選出m個物品的方案數。舉個例子,從(1,2,3) 三個物品中選擇兩個物品可以有(1,2),(1,3),(2,3)這三種選擇方法。根據組合數的定 義,我們可以給出計算組合數的一般公式:
其中n! = 1 × 2 × · · · × n
小蔥想知道如果給定n,m和k,對於所有的0 <= i <= n,0 <= j <= min(i,m)有多少對 (i,j)滿足是k的倍數。
輸入輸出格式
輸入格式:
第一行有兩個整數t,k,其中t代表該測試點總共有多少組測試數據,k的意義見 【問題描述】。
接下來t行每行兩個整數n,m,其中n,m的意義見【問題描述】。
輸出格式:
t行,每行一個整數代表答案。
輸入輸出樣例
輸入樣例#1:1 2 3 3輸出樣例#1:
1輸入樣例#2:
2 5 4 5 6 7輸出樣例#2:
0 7
說明
【樣例1說明】
在所有可能的情況中,只有是2的倍數。
【子任務】
終於開始填noip的坑了.
分析:其實對於組合數,我們有一個公式:c[i][j] = c[i-1][j-1] + c[i-1][j],但是如果n達到2000,這個數可能會超級大,於是蒟蒻的我在考場上寫高精度......其實完全不需要,既然要求mod k = 0的個數,那麽我們可以利用公式(a + b) mod c == (a mod c + b mod c) mod c,然後每算出一個c,我們就能根據它是不是0來判斷它是不是k的倍數,那麽如何求c[0][0]到c[n][m]整除k的個數呢?一般而言,求和要用到前綴和,本題要用到二維前綴和,sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + ok[i][j],優化一下時間,看到t很大,先預處理(2000,2000)的組合數即可.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <string> using namespace std; intt, k, n, m,ans[2010][2010],c[2010][2010],ok[2010][2010]; void init() { c[0][0] = 1; for (int i = 1; i <= 2000; i++) { c[i][0] = 1; for (int j = 1; j <= i; j++) { c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % k; if (c[i][j] == 0) ok[i][j] = 1; } } for (int i = 1; i <= 2000; i++) for (int j = 1; j <= 2000; j++) ans[i][j] = ans[i - 1][j] + ans[i][j - 1] - ans[i - 1][j - 1] + ok[i][j]; } int main() { scanf("%d%d", &t, &k); init(); for (int i = 1; i <= t; i++) { scanf("%d%d", &n, &m); printf("%d\n", ans[n][m]); } return 0; }
noip2016 組合數問題