洛谷[NOIP 2016 提高组] 组合数问题

题目链接

题目背景

NOIP2016 提高组 D2T1

题目描述

组合数 ( n m ) \binom{n}{m} (mn) 表示的是从 n n n 个物品中选出 m m m 个物品的方案数。举个例子,从 ( 1 , 2 , 3 ) (1,2,3) (1,2,3) 三个物品中选择两个物品可以有 ( 1 , 2 ) , ( 1 , 3 ) , ( 2 , 3 ) (1,2),(1,3),(2,3) (1,2),(1,3),(2,3) 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 ( n m ) \binom{n}{m} (mn) 的一般公式:

( n m ) = n ! m ! ( n − m ) ! \binom{n}{m}=\frac{n!}{m!(n-m)!} (mn)=m!(nm)!n!

其中 n ! = 1 × 2 × ⋯ × n n!=1\times2\times\cdots\times n n!=1×2××n;特别地,定义 0 ! = 1 0!=1 0!=1

小葱想知道如果给定 n , m n,m n,m k k k,对于所有的 0 ≤ i ≤ n , 0 ≤ j ≤ min ⁡ ( i , m ) 0\leq i\leq n,0\leq j\leq \min \left ( i, m \right ) 0in,0jmin(i,m) 有多少对 ( i , j ) (i,j) (i,j) 满足 k ∣ ( i j ) k\mid\binom{i}{j} k(ji)

输入格式

第一行有两个整数 t , k t,k t,k,其中 t t t 代表该测试点总共有多少组测试数据, k k k 的意义见问题描述。

接下来 t t t 行每行两个整数 n , m n,m n,m,其中 n , m n,m n,m 的意义见问题描述。

输出格式

t t t 行,每行一个整数代表所有的 0 ≤ i ≤ n , 0 ≤ j ≤ min ⁡ ( i , m ) 0\leq i\leq n,0\leq j\leq \min \left ( i, m \right ) 0in,0jmin(i,m) 中有多少对 ( i , j ) (i,j) (i,j) 满足 k ∣ ( i j ) k\mid\binom{i}{j} k(ji)

样例 #1

样例输入 #1

1 2
3 3

样例输出 #1

1

样例 #2

样例输入 #2

2 5
4 5
6 7

样例输出 #2

0
7

提示

【样例1说明】

在所有可能的情况中,只有 ( 2 1 ) = 2 \binom{2}{1} = 2 (12)=2 一种情况是 2 2 2 的倍数。

【子任务】

洛谷[NOIP 2016 提高组] 组合数问题_第1张图片

  • 对于全部的测试点,保证 0 ≤ n , m ≤ 2 × 1 0 3 0 \leq n, m \leq 2 \times 10^3 0n,m2×103 1 ≤ t ≤ 1 0 4 1 \leq t \leq 10^4 1t104

题目分析:这道题暴力算的话会超时,方法描述:对于每组测试数据
(n,m)遍历所有可能的 (i,j),计算组合数 (i,j),并判断是否能被 k 整除。每次查询都需要重新遍历和计算。
时间复杂度:
对于每组测试数据,需要遍历 O(n×m)对于(i,j)。如果有 t 组测试数据,总时间复杂度为:O
(t×n×m)对于 题目数据超时了,查询部分时间复杂度过高。一个很容易想到的优化便是:在预处理时,同时预处理出每个 n , m 对应的答案,将查询部分优化到 O ( 1 ) ,用到了二维前缀和

#include
using namespace std;
typedef long long ll;
const int N = 2010;
int t,n,k,m;
ll a[N][N],c[N][N];
 
void Init()
{
	c[0][0] = c[1][0] = c[1][1] = 1;
	
	for(int i = 2; 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;
			a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1];
			if(!c[i][j]) a[i][j] ++;
		}
		a[i][i + 1] = a[i][i];
	}
}
ll read()
{
	ll s=0,f=1;
	char ch=getchar();
	
	while (ch<'0'||ch>'9')
	{
   	   if (ch=='-') f=-1;
	   ch=getchar();
	}
	while (ch>='0'&&ch<='9')
	{
	   s=s*10+ch-'0';
	   ch=getchar();
	}
	return s*f;
}

void sol()
{
   n = read(),m = read();
   if(m > n) cout<

你可能感兴趣的:(洛谷数学1基础数学问题,算法,数论,c++)