【题解】代码源 2025 Summer day6 B 乘积
dmy 2025 Summer day6 B
题意
给定两个数 \(n, m\),求有多少长度为 \(2m\) 的序列 \(A\) 满足 \(A\) 中每个元素都是 \(n\) 的因数,且 \(A\) 所有项的乘积不超过 \(n^m\)。答案对 \(998244353\) 取模。
\(n\le 10^9,m\le 100\)。
题解
知识点:组合数学,容斥原理,二项式反演。
爆标了,是最优解,写篇题解纪念一下。
好题,加强一下数据范围就更好了。
要求的是有多少个长度为 \(2m\) 的 \(A\),满足:
且 \(\forall i\in[1,2m]\),有 \(A_i|n\)。
很怪异又刻意的条件,这启示着去挖掘一些潜在的性质。
对于一个正整数 \(x\),如果 \(y|x\),那么也有 \(\frac{x}{y}|x\),也就是说,数的约数是成对存在的(这里忽略 \(x\) 是完全平方数导致存在 \(y^2=x\) 的情况)。
那就会得到一个结论,对于一个正整数 \(x\),其约数中严格小于 \(\sqrt{x}\) 的数量与严格大于 \(\sqrt{x}\) 的数量完全相同。
注意到 \(\sqrt{n^{2m}}=n^m\),故尝试把上式的 \(A_i\) 替换为 \(\frac{n}{A_i}\),得到:
又因为,
所以,
类比上面的结论,可以得出,满足乘积 \(\ge n^m\) 的 \(A\) 数量与 \(\le n^m\) 的 \(A\) 数量是相同的,进一步地,满足乘积严格大于 \(n^m\) 的 \(A\) 数量与严格小于 \(n^m\) 的 \(A\) 数量是相同的。
接下来进行一点小小的代数推导。
设 \(n\) 的约数个数为 \(q\),则 \(A\) 序列总共有 \(q^{2m}\) 种生成方式。
设满足乘积严格大于 \(n^m\) 的 \(A\) 数量与严格小于 \(n^m\) 的 \(A\) 数量为 \(cnt\),乘积恰好等于 \(n^m\) 的 \(A\) 数量为 \(eq\),有如下等量关系:
则答案 \(ans=cnt+eq=\frac{q^{2m}-eq}{2}+eq=\frac{q^{2m}+eq}{2}\)。
也就是说,只需要求出 \(eq\) 就能得到答案。
对 \(n\) 唯一分解得到 \(n=\prod_{i=1}^k p_i^{c_i}\),则 \(n^m=\prod_{i=1}^k p_i^{c_i\times m}\)。
容易发现,每个质因子互相独立,考虑对每个因子分别单独求解方案数,最后乘法原理乘起来就行了。
设当前考虑到的 \(n^m\) 的质因子为 \(p\),指数为 \(c\times m\),设 \(A_i=B_i\times p^{d_i}\),则要求的的是满足如下条件的序列 \(d\) 个数:
第一眼看上去,这不就是排列组合经典套路隔板法吗?其实还需要注意到一条限制,是 \(d_i\le c\) 而非 \(d_i\le c\times m\),这是一个带限制的问题,直接硬做好像没有除了 dp 之外的解法了(当然这个数据范围 dp 能过)。
于是考虑二项式反演(其实推到最后就是普通的多步容斥,不过我习惯用这个去推)。
设 \(g_x\) 表示将 \(c\times m\) 分配给 \(2m\) 个位置,恰好有 \(x\) 个位置的值严格大于 \(c\) 的方案数(\(x\) 个位置不合法)。
设 \(f_x\) 表示将 \(c\times m\) 分配给 \(2m\) 个位置,钦定有 \(x\) 个位置的值严格大于 \(c\) ,其他位置正常分配的方案数。
二者满足如下关系:
系数 \(\binom{i}{x}\) 表示恰好 \(i\) 个不合法的情况在 \(f_x\) 的计算中会被钦定 \(\binom{i}{x}\) 次。
对其进行二项式反演,得到,
接着考虑用另一种方法表示 \(f_x\),钦定 \(x\) 个位置严格大于 \(c\),可以看作给这 \(x\) 个位置提前分配 \(c+1\),然后再统一分配,这样这 \(x\) 个位置最后一定是会严格大于 \(c\) 的。
至于其他位置有没有不合法的情况,不需要理睬,遵从 \(f_x\) 的定义就行了。
所以可以推得,
前面的 \(\binom{2m}{x}\) 很好理解,就是 \(2m\) 个位置选 \(x\) 个。
对于后面的式子,首先,这 \(x\) 个位置提前分配 \(c+1\) 之后,剩下了 \(c\times m-x\times (c+1)\) 个。
然后用隔板法,用 \(2m-1\) 个隔板插入 \(c\times m-x\times (c+1)-1\) 个空隙,从而划分出 \(2m\) 个区域,隔板间的空位数量就是分配的值,不过这样保证的是每个位置至少分到 \(\ge 1\),在这里可以分到 \(0\),所以再加 \(2m\) 个空隙,相当于每个位置能多分到 \(1\) 个,向左偏移 \(1\) 位就可以看作能分配到 \(0\) 了。
既然要满足条件,那么就是 \(0\) 个不合法的,所以对于该质因子答案为:
退化为了一般的容斥原理。
则总的答案为:
这里把质因子个数 \(k\) 和 \(n\) 分解后质因子最大指数 \(c_{max}\) 看作 \(O(\log n)\) 级别。
复杂度分为三部分,质因数分解 \(O(\sqrt{n})\),组合数预处理 \(O(m\log n)\),容斥 \(O(m\log n)\),则总复杂度为 \(O(\sqrt{n}+m\log n)\),瓶颈在于质因数分解。
代码中的 \(c_i\) 是 \(n^m\) 分解后的指数,也就是说提前乘了 \(m\),注意甄别。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 6991
#define M 305
#define int long long
const int mod=998244353;
int n,m,len;
inline int qpow(int a,int b){
int ans=1;
while(b){
if(b&1){
ans=ans*a%mod;
}
a=a*a%mod;
b>>=1;
}
return ans;
}
int c[N],cnt,fac[N],iv[N];
inline void init(int lim){
fac[0]=1;
rep(i,1,lim){
fac[i]=fac[i-1]*i%mod;
}
iv[lim]=qpow(fac[lim],mod-2);
per(i,0,lim-1){
iv[i]=iv[i+1]*(i+1)%mod;
}
}
inline int C(int n,int m){
if(n<m){
return 0;
}
return fac[n]*iv[m]%mod*iv[n-m]%mod;
}
inline void sol(){
init(6978);
int l=2*m,tot=qpow(len,l);
int tmp=n;
rep(i,2,sqrt(tmp)){
if(tmp%i==0){
cnt++;
int res=0;
while(tmp%i==0){
tmp/=i;
res++;
}
c[cnt]=res*m;
}
}
if(tmp>1){
cnt++;
c[cnt]=m;
}
int res=1;
rep(i,1,cnt){
int num=0;
rep(j,0,l){
int lim=c[i]/m+1;
int tt=C(l,j)*C(c[i]-j*lim+l-1,l-1);
if(j&1){
num=(num-tt+mod)%mod;
}
else{
num=(num+tt)%mod;
}
}
res=res*num%mod;
}
int ans=(tot+res)%mod*qpow(2,mod-2)%mod;
cout<<ans<<"\n";
}
signed main(){
// freopen("me.in","r",stdin);
// freopen("B.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
rep(i,1,sqrt(n)){
if(n%i==0){
len++;
if(i*i!=n){
len++;
}
}
}
sol();
return 0;
}

浙公网安备 33010602011771号