洛谷题单指南-数学基础问题-P2822 [NOIP2016 提高组] 组合数问题
原题链接:https://www.luogu.com.cn/problem/P2822
题意解读:本质上是要计算所有组合数C(i,j),
解题思路:
本题要解决两个问题:
1、 计算C(0~n、0~m)范围内所有的组合数,只用保留%k的结果
2、统计有多少个C(i,j)等于0,
#include <bits/stdc++.h>
using namespace std;
const int N = 2005;
int c[N][N];
int t, k, n, m;
int main()
{
cin >> t >> k;
//预计算所有组合数 % k
for(int i = 0; i <= 2000; i++)
{
for(int j = 0; j <= i; j++)
{
if(j == 0) c[i][j] = 1 % k;
else c[i][j] = (c[i - 1][j] % k + c[i - 1][j - 1] % k) % k;
}
}
while(t--)
{
cin >> n >> m;
int cnt = 0;
for(int i = 0; i <= n; i++)
{
int e = min(i, m);
for(int j = 0; j <= e; j++)
{
if(c[i][j] == 0)
{
cnt++;
}
}
}
cout << cnt << endl;
}
return 0;
}
C(0,0) | ||
C(1,0) | C(1,1) | |
C(2,0) | C(2,1) | C(2,2) |
C(3,0) | C(3,1) | C(3,2) |
要枚举并计数
即要统计(0,0)到(3,2)区间范围有多少个0,可以采用二维前缀和预计算,复杂度为O(1),注意排除掉j>i的数据影响。
二维前缀和:
已知数组a[i][j],前缀和数组s[i][j],s[i][j]表示a数组 1-i行、1-j列所有元素的和
递推公式:s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][[j]
作用:求区间和
(x1,y1)到(x2,y2)之间所有元素的和为s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1]
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2005;
int c[N][N];
int s[N][N]; //前缀和数组
int t, k, n, m;
int main()
{
cin >> t >> k;
//预计算所有组合数 % k
for(int i = 0; i <= 2000; i++)
{
for(int j = 0; j <= i; j++)
{
if(j == 0) c[i][j] = 1 % k;
else c[i][j] = (c[i - 1][j] % k + c[i - 1][j - 1] % k) % k;
}
}
//预计算前缀和,前缀和数组下标从1开始,对应c数组下标要减1
for(int i = 1; i <= 2001; i++)
{
for(int j = 1; j <= 2001; j++)
{
int a = 0;
if(j - 1 <= i - 1 && c[i - 1][j - 1] == 0) a = 1;
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a;
}
}
while(t--)
{
cin >> n >> m;
cout << s[n + 1][min(n + 1, m + 1)] << endl;
}
return 0;
}