问题 K: 数学问题
时间限制: 1 Sec 内存限制: 128 MB
提交: 381 解决: 24
[状态] [提交] [命题人:外部导入]
题目描述
我们高中曾经学过何为组合数。 那么,给出整数n,m,g,聪明的你能否求出有多少整数对(i,j),满足g整除吗?(其中0≤i≤n,0≤j≤min(i,m))。 (提示:n!=1×2×⋯×n;特别地,0!=1。)
输入
第一行一个整数T(T<=104 ),表示测试数据的组数;
第二行一个整数g(1<g<=25);
接下来T行每行两个整数n,m(n,m<=2000);
n,m,g的意义见题目描述。
输出
输出T行,每行一个整数,表示有多少对整数对(i,j)满足g整除(0≤i≤n,0≤j≤min(i,m))。
样例输入 Copy
1 4 5 4
样例输出 Copy
2
利用二维前缀和 s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1],将(n,m)范围内整数对对数预先打表出来,把每次查询优化到 O(1)。
坑点:n 有可能 < m
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 2e3 + 10;
ll a[N][N];
ll sum[N][N];
ll g;
void init()
{
memset(a, 0, sizeof(a));
a[0][0] = 1;
for(int i = 1; i < N; ++i)
{
a[i][0] = 1;
a[i][i] = 1;
for(int j = 1; j < i; ++j)
{
a[i][j] = ((a[i - 1][j] % g) + (a[i - 1][j - 1] % g)) % g;
}
}
}
void solve()
{
memset(sum, 0, sizeof(sum));
for(int i = 1; i < N; ++i)
{
for(int j = 1; j <= i; ++j)
{
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
if(a[i][j] == 0)
{
sum[i][j]++;
}
}
sum[i][i + 1] = sum[i][i];
}
}
int main()
{
int n, t, m;
while(~scanf("%d", &t))
{
scanf("%lld", &g);
init();
solve();
while(t--)
{
scanf("%d%d", &n, &m);
if(n < m)
m = n;
cout<<sum[n][m]<<'\n';
}
}
return 0;
}