题目背景
998244353最优美的性质莫过于它是个完美恶臭数:
998244353=( 114514 * ( 54-1+114 * (1+145+1+4) ) ) + ( 4+11451 * (4-1-15+14) ) + ( 11+4154 + (141+541) ) + (4-1-15+14)
之所以称为完美,如果您仔细观察,您会发现每一个括号里114514都出现了正整数次!
今天,你需要用这个恶臭数进行一系列的复杂运算(或许?)
题目描述
米4达的替身名为"性感手枪",可以操纵子弹的飞行轨迹。一天在与"绯红之王"的战斗中米4达打光了子弹,所以他不得不重新装弹。他一次性掏出了N发子弹,编号从1到N,他需要从中选择M枚子弹。除此之外,他不会选择其中编号带有4的子弹,例如4号子弹,44号子弹,444号子弹。米4达一共有多少种选择子弹的方案
输入格式
第一行输入一个正整数T(1<=T<=1e5)表示数据组数
接下来输入一行2个正整数N和M(1<=N,M<=2000),含义如题
输出格式
输出T行,每行输出可选方案数
对于每个数据,答案对998244353取模后输出
输入输出样例
输入 #1复制
3
5 3
3 1
4 4
输出 #1复制
4
3
0
说明/提示
请注意特判
一道组合数学,所用的知识是高中学的。但要注意到的是本题的问询量很高(1e5次问询),如果对于每次问询都进行常规的组合计算会导致超时
这里注意到,如果a和b的范围较小,所以可以利用组合递推式先预处理出所有a,b的答案
由高中知识,有组合递推式:

for (int j = 0; j <= i; j ++ )
if (!j)
c[i][j] = 1;
else
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
实现预处理
此外,还需要注意条件特判,如果可用子弹数小于所需子弹数,那么可选的方法就是0,此时应输出0
参考代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2010, mod = 998244353;
int c[N][N];
int num[N];//i个子弹中实际可用的数量
bool check(int x) {
while (x) {
int k = x % 10;
if (k == 4)
return false;
x /= 10;
}
return true;
}
void init() {
num[0] = 0;
for (int i = 1; i <= 2000; i++) {
if (check(i)) {
num[i] = num[i - 1] + 1;
} else {
num[i] = num[i - 1];
}
}
for (int i = 0; i < N; i ++ )
for (int j = 0; j <= i; j ++ )
if (!j)
c[i][j] = 1;
else
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
int main() {
int n;
init();
scanf("%d", &n);
while (n -- ) {
int a, b;
scanf("%d%d", &a, &b);
a = num[a];
if (a < b) {
cout << "0" << endl;
continue;
}
printf("%d\n", c[a][b]);
}
return 0;
}