Codeforces --Modular Stability(逆元+组合数+快速幂)

本文探讨了在特定模数关系下,寻找成倍数关系数字组合的问题。通过分析最小元素及其倍数,利用逆元组合数预处理,解决了在一定范围内找出k个数的成倍数关系组合数目。介绍了快速幂、阶乘及其逆元的计算方法,并提供了AC代码实现。

题目:

在这里插入图片描述
在这里插入图片描述

思路:

  依题意如果将mod的顺序任意变化,答案不变,那么所有数必须要是最小的数的倍数。假设模数没有倍数关系,那么连续取模就会破坏数据。问题取决于最小元素a[1]a[1]a[1],之后就是在a[1]a[1]a[1]的倍数中取k−1k-1k1个,根据逆元组合数预处理出阶乘的逆元.
对阶乘的逆元有:inv[n!]=inv[(n+1)!]∗(n+1)%modinv[n!]=inv[(n+1)!]∗(n+1)\%modinv[n!]=inv[(n+1)!](n+1)%mod

inv[n!]=1n!=(n!)mod−2%modinv[n!]=\frac{1}{n!}=(n!)^{mod-2}\%modinv[n!]=n!1=(n!)mod2%mod

=1(n+1)!∗(n+1)=((n+1)!mod−2%mod)∗(n+1)=\frac{1}{(n+1)!}*(n+1)=((n+1)!^{mod-2}\%mod)*(n+1)=(n+1)!1(n+1)=((n+1)!mod2%mod)(n+1)

=inv[(n+1)!]∗(n+1)%mod=inv[(n+1)!]*(n+1)\%mod=inv[(n+1)!](n+1)%mod
测试一:
7 3
16
分析:
n=7,也就是从1,2,3,4,5,6,7中拿出k个成倍数关系的数字
当最小数a[1]=1的时候,从剩下的a[1]的倍数(n/1-1=6)中拿出(k-1=2)个数,有C62=15C_{6}^{2}=15C62=15
当最小数a[1]=2的时候,从剩下的a[1]的倍数(n/2-1=2)中拿出(k-1=2)个数,有C22=1C_{2}^{2}=1C22=1
当最小数a[1]=3的时候,从剩下的a[1]的倍数(n/3-1=1)中拿出(k-1=2)个数,有C12=0C_{1}^{2}=0C12=0

综上n=7,k=3的时候,有15+1=16种可能
我们可以推导出最终的结果为∑i=1nCni−1k−1\sum_{i=1}^{n}C_{\frac{n}{i}-1}^{k-1}i=1nCin1k1

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353,maxn=5e5+5;
ll inv[maxn],fac[maxn],n,k,ans=0;
ll qpow(ll a,ll b) { //快速幂模板 
	ll res = 1,base = a;
	while(b) {
		if(b&1) res = res*base%mod;
		base = base*base%mod;
		b >>= 1;
	}
	return res;
}
void init() {  //初始化 求阶乘和阶乘的逆元 
	fac[0]=1;
	for(int i=1; i<maxn; i++) fac[i]=(fac[i-1]*i)%mod;
	int t=5e5;
	inv[t]=qpow(fac[t],1LL*(mod-2))%mod;
	for(int i=t-1; i>=0; i--) inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(ll x,ll y) { //组合数模板 
	if(x<y) return 0;
	ll res=1;
	y=min(x-y,y);
	for(int i=x; i>=x-y+1; i--) res=res*i%mod;
	res=res*inv[y]%mod;
	return res;
}
int main() {
	cin>>n>>k;
	init();
	for(int i=1; i<=n; i++) ans=(ans+C(n/i-1,k-1))%mod;
	cout<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值