题解:P10986 [蓝桥杯 2023 国 Python A] 2023
[蓝桥杯 2023] 2023
题目分析
题目大意
给出两个正整数
题目思路
使用二项式反演将问题转化为计算至少出现
组合计数:
- 在
n 位中选出k 个2023 可以通过确定首位置的方式来确定2023 的位置,则转化成在n-3k 个数里选出k 个数; - 选出
k 个2023 ,则剩余n-4k 位,由于允许前导零存在,故每一位都可以填0-9 的任何数字,故剩余项方案数为10^{n-4k} 。
综上所述,在
也可以使用隔板法来得出:
- 有
k 个2023 作为隔板,则有k+1 个可摆放的位置; - 有
n-4k 个“位”的自由位置。
使用隔板法:将
令
相当于在
容斥原理:
令
二项式反演:
令:
则存在基本关系:
解释:若一个数字串实际有
二项式反演定理:
变体:
故可得出:
其中,
最终公式:
其中
知识总结
数学知识
- 二项式定理
- 二项式反演
- 容斥原理
- 组合数学概念、表示及公式
- 乘法原理
- 逆元概念
- 费马小定理
- 模运算公式
算法知识
- 快速幂求逆元
- 求组合数(阶乘与逆元)
算法实现
预处理:
-
-
-
- 使用快速幂求逆元
主函数循环:
- 遍历可能的
2023 的个数k (从m 到\lfloor n/4 \rfloor ); - 计算
g(k)={n-3k\choose k}\times 10^{n-4k} ; - 计算二项式系数
{k \choose m} ; - 根据容斥原理调整正反
(-1)^{k-m} ; - 累计到
ans 最终答案,注意:由于容斥原理会加会减,且模运算会改变数的实际大小,为防止出现负数,在模运算之前加上模数,ans=(ans+(long long)c_1*c_2*sign+mod)%mod。
时间复杂度
AC Code。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10,mod = 998244353;
typedef long long ll;
int fact[N],infact[N],pow10[N];//fact[i]--i 的阶乘,infact[i]--i 的阶乘逆元,pow10[i]--10 的 i 次幂
int qmi(int a,int k)//快速幂求逆元
{
int res=1;
while (k)
{
if (k&1) res=(ll)res*a%mod;
a=(ll)a*a%mod;
k>>=1;
}
return res;
}
void init(int n)
{
fact[0]=infact[0]=pow10[0]=1;//初始化
for (int i=1;i<=n;i++)
{
fact[i]=(ll)fact[i-1]*i%mod;//阶乘计算
pow10[i]=(ll)pow10[i-1]*10%mod;//10 的幂计算
}
infact[n]=qmi(fact[n],mod-2);
for (int i=n-1;i>=1;i--)
infact[i]=(ll)infact[i+1]*(i+1)%mod;//阶乘逆元计算
}
int main()
{
int n,m,ans=0,sign=-1;
scanf("%d%d",&n,&m);
init(n);
for (int i=m;i<=n/4;i++)
{
sign*=-1;
int a=n-i*3;
int c1=(ll)fact[a]*infact[i]%mod*infact[a-i]%mod*pow10[n-4*i]%mod;
int c2=(ll)fact[i]*infact[m]%mod*infact[i-m]%mod;
ans=(ans+(ll)c1*c2*sign+mod)%mod;//sign 有时为 -1,由于模运算,要加 mod,在模 mod,以防为负数
}
printf("%d",ans);
return 0;
}