【题解】代码源 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\),满足:

\[\displaystyle \prod_{i=1}^{2m} A_i \le n^m \]

\(\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}\),得到:

\[\displaystyle \prod_{i=1}^{2m} \frac{n}{A_i} \]

\[=\frac{n^{2m}}{\displaystyle \prod_{i=1}^{2m} A_i} \]

又因为,

\[\displaystyle \prod_{i=1}^{2m} A_i \le n^m \]

所以,

\[\displaystyle \prod_{i=1}^{2m} \frac{n}{A_i}\ge m \]

类比上面的结论,可以得出,满足乘积 \(\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\),有如下等量关系:

\[2cnt+eq=q^{2m} \]

则答案 \(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\) 个数:

\[\displaystyle \sum_{i=1}^{2m} d_i=c\times m \]

第一眼看上去,这不就是排列组合经典套路隔板法吗?其实还需要注意到一条限制,是 \(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\) ,其他位置正常分配的方案数。

二者满足如下关系:

\[\displaystyle f_x=\sum_{i=x}^{2m} \binom{i}{x} g_i \]

系数 \(\binom{i}{x}\) 表示恰好 \(i\) 个不合法的情况在 \(f_x\) 的计算中会被钦定 \(\binom{i}{x}\) 次。

对其进行二项式反演,得到,

\[\displaystyle g_x=\sum_{i=x}^{2m} (-1)^{i-x}\binom{i}{x} f_i \]

接着考虑用另一种方法表示 \(f_x\),钦定 \(x\) 个位置严格大于 \(c\),可以看作给这 \(x\) 个位置提前分配 \(c+1\),然后再统一分配,这样这 \(x\) 个位置最后一定是会严格大于 \(c\) 的。

至于其他位置有没有不合法的情况,不需要理睬,遵从 \(f_x\) 的定义就行了。

所以可以推得,

\[f_x=\binom{2m}{x} \times \binom{c\times m-x\times (c+1)+2m-1}{2m-1} \]

前面的 \(\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\) 个不合法的,所以对于该质因子答案为:

\[\displaystyle g_0=\sum_{i=0}^{2m} (-1)^{i} \binom{2m}{x} \times \binom{c\times m-x\times (c+1)+2m-1}{2m-1} \]

退化为了一般的容斥原理。

则总的答案为:

\[\frac{q^{2m}+\displaystyle \prod_{j=1}^k \sum_{i=0}^{2m} (-1)^{i} \binom{2m}{x} \times \binom{c_j\times m-x\times (c_j+1)+2m-1}{2m-1}}{2} \]

这里把质因子个数 \(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;
}
posted @ 2025-08-04 15:26  Lucyna_Kushinada  阅读(38)  评论(0)    收藏  举报