首先,膜拜邝斌大佬和咖啡鸡大佬。
组合数学的板子来自咖啡鸡的某份代码。
CSL likes playing tricks very much. The unlucky TL always becomes the target of CSL’s tricks.
Today CSL came to trick TL again. He saw that TL has a very very large checkerboard and many glass balls. So he have an idea. He said to TL:
"What a big chessboard you have… How about play a game with me ? I will give you and ,and you can choose an arbitrarily large square area on the chessboard (assuming you choose a square area) and place a number of glass balls on each of the squares (each square should place not less than glass balls). Your placement needs to be satisfied:
If we choose squares of different rows and columns, then no matter how we choose, the total number of glass balls in this squares should always be the same, and should not greater then .
You just need to tell me how many ways you have to satisfy this. If you can’t tell me, the chessboard and glass balls will all belong to me !
TL doesn’t want to give the chessboard and glass ball to CSL, but he knows that the clever CSL has already worked out the answer in his mind. Can you help him solve this problem?
The first line of the input is a single integer T(T≤5) indicating the number of test cases.
Each of the following lines contains 2 integers and (meaning as description)
T≤5 , 1≤n,m≤2000
For each test case, output the answer in a single line. because the answer may be very big, so just print the result after mod
输入
5
1 1
2 1
3 1
4 1
5 1
输出
1
3
9
26
73
给定n,m。求k*k的矩阵,1<=k<=n,矩阵内的每个元素都不小于m,且矩阵内不同行不同列的元素相加都为一个定值T,且T<=n。问这样的矩阵有多少种,MOD998244353.
kuangbin说:
我们计“选择大小为?∗?的棋盘,每个方格放的玻璃球数都不小于?且每不同行不同列的方 格内的玻璃球数量的总和均为?”的方案数为??(?,?),那么很显然的总的方案数即为
行叭,假装很显然。(看懂之后真的很显然)
kuangbin又说:
行叭,看懂之后也是比较容易想到的。
因为矩阵内每个元素都有一个最小值m,我们把每个元素都减去它的最小值,就可以得出最小值为0的矩阵,故等式成立。
kuangbin:
kuangbin又说:
因为Ai矩阵每一行都相同,Bi矩阵每一列都相同,所以当我们取不同行不同列数之和必定相同。
kuangbin说:
这个应该比较好理解,因为Ai每一行都相同,Bi每一列都相同,所以直接对ai bi求和就是T。
kuangbin又容易道:
我觉得这玩意儿真不容易啊。。。
首先我们将ai和bi当成一个数组考虑,我们可以想到,我们应该将T个数,分配到2k个格子里,格子里的数可以是零。把一堆东西分配到几个格子里,我们首先想到的就是高中经常用的隔板法,但是隔板法要求每个容器必须有一个东西,所以我们加2k个东西,然后每个容器至少有一个东西,最后再减掉这一个东西就行了。即得出上图的公式。
但是我们会发现,我们计算的方案数有重复,所以我们需要把重复的方案减掉一次,怎么减呢,这时候~
kuangbin大佬又来了:
这个等式真的很显然,ai>=1的话就不会出现负数,bi正常加一即可。此时我们很容易发现,如果把ai全部先减去1,突然发现!!!这好像又是一个隔板法!!!因为我们已经把ai全部减了一个1了,所以此时要分配的一堆东西只剩T+K个了,然后再用隔板法把它们分配到2k个容器里面。这些就是重复了的方案数。减掉即可。
然后我们枚举T和k暴力求解即可。
出现减法的MOD的时候别忘了+MOD
#include
using namespace std;
typedef long long ll;
const int maxn=3e4+5;
const ll MOD=998244353;
int f[maxn],nf[maxn];
int pow_(int x,int y)
{
int ret=1;
while (y)
{
if (y&1)
ret=1ll*ret*x%MOD;
x=1ll*x*x%MOD;
y>>=1;
}
return ret;
}
void init()
{
f[0]=nf[0]=1;
for (int i=1; i<maxn; i++)
f[i]=1ll*f[i-1]*i%MOD;
nf[maxn-1]=pow_(f[maxn-1],MOD-2);
for (int i=maxn-1; i; i--)
nf[i-1]=1ll*nf[i]*i%MOD;
}
int C(int x,int y)
{
return 1ll*f[x]*nf[y]%MOD*nf[x-y]%MOD;
}
int main()
{
init();
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
ll ans=0;
for(int k=1;k*m<=n;k++)
{
int tt=n-k*m;
for(int t=0;t<=tt;t++)
{
ans=(ans+C(t+2*k-1,2*k-1))%MOD;
if(t>=k)
{
ans=(ans-C(t+k-1,2*k-1)+MOD)%MOD;
}
}
}
printf("%lld\n",ans);
}
return 0;
}