题解:AT_abc132_f [ABC132F] Small Products

题目

思路

首先考虑一个暴力的 DP,我们用 \(dp_{i,j}\) 表示长度为 \(i\),结尾为 \(j\) 时的方案数,

动态转移方程为:

\[dp_{i,j}=\sum_{p=1}^{\lfloor \frac{n}{j} \rfloor}dp_{i-1,p} \]

就算加个前缀和优化,时间复杂度也为 \(\mathcal{O}(nk)\),显然过不了,怎么办呢?

注意到 \(dp_{i,j_1}\)\(dp_{i,j_2}\)\(\lfloor \frac{n}{j_1} \rfloor = \lfloor \frac{n}{j_2} \rfloor\) 时是相等的,
不妨将他们放在一起算。

我们先用整除分块处理出 \(n\) 作为被除数,且除数不超过 \(n\) 的所有的商(此处指带余数除法的商,下文亦然),用 \(s_i\) 表示第 \(i\) 小的那个商,个数设为 \(sd\)。同时处理出其对应的除数的个数,用 \(g_i\) 表示。

则状态优化为 \(dp_{i,j}\)\(i\) 仍为长度,\(j\)\(n\) 除以序列最后值的商。

先不急着求状态转移方程,我们先证一个引理:对于所有 \(i \in [1,sd]\),有:

\[\lfloor \frac{n}{s_i} \rfloor=s_{sd-i+1} \]

易证对于 \(i \in [1,sd]\),有:

\[\lfloor \frac{n}{\lfloor\frac{n}{s_i}\rfloor}\rfloor=s_i \]

从整除分块的过程中观察,这是显而易见的,不再赘述。

可以确定对于 \(s_1\)\(s_{sd}\) 有:

\[\lfloor \frac{n}{s_1} \rfloor=s_{sd} \]

\[\lfloor \frac{n}{s_{sd}} \rfloor=s_1 \]

因为 \(s_1\) 一定为一,\(s_{sd}\) 一定为 \(n\)

需要先解释一下的是,对于 \(i \in [1,sd]\),一定有某个 \(j \in [1,sd]\) 满足
\(\lfloor \frac{n}{s_i} \rfloor=s_{j}\)。因为 \(\lfloor \frac{n}{s_i} \rfloor\) 也是 \(n\) 作为某个被除数的一个商,一定包含在 \(s\) 中。

假设对于 \(i\) 已知 \(j\),使得 \(\lfloor \frac{n}{s_{j}}\rfloor=s_i\),则 \(\lfloor \frac{n}{s_{i}} \rfloor=s_j\)

\(x\) 使得 \(\lfloor \frac{n}{s_{i+1}} \rfloor=s_{x}\),则\(\lfloor \frac{n}{s_{x}} \rfloor=s_{i+1}\)

先证 \(x<j\)

因为

\[\lfloor \frac{n}{s_{j}} \rfloor=s_i \]

同时

\[s_i<s_{i+1} \]

所以 \(s_x<s_j\),即 \(x<j\)

然后我们采用反证法,假设 \(x<j-1\)

考虑 \(\lfloor \frac{n}{s_{j-1}} \rfloor\)\(s\) 中的位置,先设其为 \(k\)

因为

\[x<j-1<j \]

所以有

\[\lfloor \frac{n}{s_{j}} \rfloor\le\lfloor \frac{n}{s_{j-1}} \rfloor \le\lfloor \frac{n}{s_{x}}\rfloor \]

结合上面的 \(\lfloor \frac{n}{\lfloor\frac{n}{s_i}\rfloor}\rfloor=s_i\)
\(\lfloor \frac{n}{s_{j}} \rfloor=\lfloor \frac{n}{s_{j-1}} \rfloor\)\(\lfloor \frac{n}{s_{j-1}} \rfloor=\lfloor \frac{n}{s_{x}} \rfloor\) 时,一定有 \(j=j-1\)\(j-1=x\),与前面矛盾。

所以

\[\lfloor \frac{n}{s_{j}} \rfloor<\lfloor \frac{n}{s_{j-1}} \rfloor <\lfloor \frac{n}{s_{x}} \rfloor \]

也就是

\[s_i<s_k<s_{i+1} \]

显然不存在这样的 \(k\),同时有 \(x<j\),所以

\[x=j-1 \]

所以当 \(\lfloor \frac{n}{s_{i}} \rfloor=s_j\) 时,有:

\[\lfloor \frac{n}{s_{i+1}}\rfloor=s_{j-1} \]

现在我们已经确定了 \(\lfloor \frac{n}{s_{1}} \rfloor=s_{sd}\)

进而有

\[\lfloor \frac{n}{s_{2}} \rfloor=s_{sd-1} \]

\[\lfloor \frac{n}{s_{3}} \rfloor=s_{sd-2} \]

\[\dots \]

\[\lfloor \frac{n}{s_{i}} \rfloor=s_{sd-i+1} \]

故引理得证。

现在我们就可以写出状态转移方程了:

对于 \(i\in[1,k],j\in[1,sd]\)

\[dp_{i,j}=\sum_{k=sd}^{sd-j+1} dp_{i-1,k}\times g_j \]

再加个前缀和优化,时间复杂度为 \(\mathcal{O}(k\sqrt{n})\)

代码

#include<iostream>
#include<cstdio>
#include<map>
#define ll long long
#define mod 1000000007
using namespace std;
ll cs[100005],cd,cg[100005],n,k,dp[105][100005];
int main(){
    scanf("%lld%lld",&n,&k);
    for(ll i=1;i<=n;i++){
        cs[++cd]=n/i;
        ll ni=n/(n/i);
        cg[cd]=ni-i+1;
        i=ni;
    }
    for(ll i=1;i<=cd;i++){
        dp[1][i]=cg[i];
    }
    for(ll i=2;i<=k;i++){
        ll qz=0,d=0;
        for(ll j=cd;j>=1;j--){
            d++;
            qz+=dp[i-1][d];
            qz%=mod;
            dp[i][j]=qz*cg[j];
            dp[i][j]%=mod;
        }
    }
    ll ans=0;
    for(ll i=1;i<=cd;i++){
        ans+=dp[k][i];
        ans%=mod;
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2025-11-08 23:21  hyx1gg  阅读(1)  评论(0)    收藏  举报