Luogu P2822 组合数问题(前缀和)
Posted coder-uranus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P2822 组合数问题(前缀和)相关的知识,希望对你有一定的参考价值。
题意
题目描述
组合数(C_n^m)表示的是从(n)个物品中选出(m)个物品的方案数。举个例子,从((1,2,3))三个物品中选择两个物品可以有((1,2),(1,3),(2,3))这三种选择方法。根据组合数的定义,我们可以给出计算组合数(C_n^m)的一般公式:
[C_n^m=frac{n!}{m!(n-m)!}]
其中(n!=1 imes 2 imes cdots imes n);特别地,定义(0!=1)。
小葱想知道如果给定(n,m)和(k),对于所有的(0leq ileq n,0leq jleq min left( i, m ight))有多少对((i,j))满足(C_i^j)是(k)的倍数。
输入输出格式
输入格式:
第一行有两个整数(t,k),其中(t)代表该测试点总共有多少组测试数据,(k)的意义见问题描述。
接下来(t)行每行两个整数(n,m),其中(n,m)的意义见问题描述。
输出格式:
共(t)行,每行一个整数代表所有的(0leq ileq n,0leq jleq min left( i,m ight))中有多少对((i,j))满足(C_i^j)是(k)的倍数。
输入输出样例
输入样例#1:
1 2
3 3
输出样例#1:
1
输入样例#2:
2 5
4 5
6 7
输出样例#2:
0
7
说明
【样例1说明】
在所有可能的情况中,只有(C_2^1=2)是(2)的倍数。
【子任务】
思路
(10)个月以前,当我和一位数竞党聊起这道题的时候,他启发我,可以利用(k)的特性来特判每一个数据点。当时的我嫌麻烦,没有这样写。如今问了(Mercury)这道题的做法,才发现正解才是(OI)思维,之前的想法太偏数学了。
首先,杨辉三角的值与组合数相同,我们可以用求杨辉三角的方法很快求出组合数。在求的过程中,组合数对(k)取模,若该位为(0),则说明它是(k)的倍数。
然后就是这道题的精髓了:用一个二维数组(s[i][j])统计组合数为(0)的情况的前缀和。转移方法是:(s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+[c[i][j]=0])。
然后直接输出前缀和就好啦。
AC代码
#include<bits/stdc++.h>
using namespace std;
int t,k,a[2005][2005],s[2005][2005];
int read()
{
int re=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
int main()
{
t=read(),k=read();
a[1][1]=1;
for(int i=2;i<=2001;i++)
{
for(int j=1;j<=i;j++) a[i][j]=(a[i-1][j-1]+a[i-1][j])%k;
for(int j=1;j<=i;j++) s[i][j]=s[i][j-1]+(!a[i][j]);
for(int j=i+1;j<=2001;j++) s[i][j]=s[i][i];
for(int j=1;j<=2001;j++) s[i][j]+=s[i-1][j];
}
while(t--)
{
int x=read(),y=read();
printf("%d
",s[x+1][y+1]);
}
return 0;
}
以上是关于Luogu P2822 组合数问题(前缀和)的主要内容,如果未能解决你的问题,请参考以下文章