题解:CF2278F Modulo and Counting

题目链接:Codeforces 洛谷

思路

直接 dp 的状态是三维的,并且没有什么优化方法,因此尝试观察性质。

\(\sum_{i=1}^{n-1}(a_i\bmod a_{i+1})=m\) 这个式子里面有取模,不好直接做,考虑用 \(x\bmod y=x-k\cdot y\ (k=\lfloor \frac{x}{y} \rfloor)\) 转化一下,就得到了 \(\sum_{i=1}^{n-1}(a_i\bmod a_{i+1})=\sum_{i=1}^{n-1}(a_i-k_i\cdot a_{i+1})=m\)。

对新式子合并同类项,可得:

\[\begin{align*} &m=\sum_{i=1}^{n-1}(a_i-k_i\cdot a_{i+1})\\ &=\sum_{i=1}^{n-1}a_i-\sum_{i=2}^n k_{i-1}\cdot a_i\\ &=\sum_{i=2}^n a_i-\sum_{i=2}^n k_{i-1}\cdot a_i+a_1-a_n\\ &=-\sum_{i=2}^n(k_{i-1}-1)a_i+a_1-a_n \end{align*} \]

于是有 \(\sum_{i=2}^n(k_{i-1}-1)a_i=a_1-a_n-m\)。分析一下右式,\((a_1)_{max}=m+2,(a_n)_{min}=1\),所以 \((a_1-a_n-m)_{max}=1\);又因为左式显然非负,所以等式两边要么是 \(0\),要么是 \(1\)。

分讨一下:

  • 如果等式两边为 \(1\)。那可以确定 \(a_1=m+2,a_n=1\),且左式中这个 \(1\) 只能分配给唯一的一个满足 \(k_{i-1}=2,a_i=1\) 的位置 \(i\),其他位置 \(j\) 都必须满足 \(k_{j-1}=1\)。位置 \(i\) 在哪里呢?考虑从右往左的最后一个 \(a_i=1\) 的位置,这里的 \(k_{i-1}=\lfloor \frac{a_{i-1}}{a_i}\rfloor=a_{i-1}\leq 2\),所以 \(a_{i-1}\) 就必须等于 \(2\),这个位置正好消耗了一个 \(1\),而其他位置 \(j\ (2\leq j<i)\) 都需要满足 \(\lfloor \frac{a_{j-1}}{a_j}\rfloor=1\) 即 \(a_{j-1}<2\cdot a_j\)。

  • 如果等式两边为 \(0\)。那所有的 \(k\) 都必须为 \(1\),且有 \(a_1=m+1,a_n=1\) 或 \(a_1=m+2,a_n=2\)。根据前一种情况的分析,只要存在 \(a\) 等于 \(1\),就必定会有位置满足 \(a_{i-1}\geq 2,a_i=1\),从而导致 \(k_{i-1}\geq 2\),不符合要求。所以情况一定为 \(a_1=m+2,a_n=2\),且所有位置 \(i\ (2\leq i\leq n)\) 都满足 \(a_{i-1}<2\cdot a_i\)。

把两种情况合并起来,限制如下:

  • \(a_1=m+2,a_n\leq 2\);

  • 对任何满足 \(2\leq i\leq n,a_i>1\) 的位置 \(i\),都有 \(a_{i-1}<2\cdot a_i\);

  • \(a_i\) 单调不增,且必须存在 \(a_i=2\) 的位置。

根据上面的推导,容易证明这些限制就是 \(\sum_{i=1}^{n-1}(a_i\bmod a_{i+1})=m\) 的充要条件。

根据这些限制就容易设计 dp 方程了。设 \(f_{i,j}\) 表示考虑到 \(i\),\(a_i=j\) 的合法情况数,并要求 \(j\geq 2\)。初状态是 \(f_{1,m+2}=1\);转移为 \(f_{i,j}=\sum_{k=j}^{2j-1}f_{i-1,k}\),可以前缀和优化;最终答案可以枚举最后一个 \(a_i=2\) 的位置,即 \(ans=\sum_{i=2}^n f_{i,2}\)。

时空复杂度均为 \(O(nm)\)。

代码

#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define ll long long
const int N=4005,P=998244353;

int n,m,f[N][N];

void solve()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m+2;j++)
            f[i][j]=0;
    scanf("%d%d",&n,&m);
    f[1][m+2]=1;
    int ans=0;
    for(int i=2;i<=n;i++)
    {
        for(int j=3;j<=m+2;j++)
            (f[i-1][j]+=f[i-1][j-1])%=P;
        for(int j=2;j<=m+2;j++)
        {
            int up=min(m+2,j*2-1);
            f[i][j]=(f[i-1][up]-f[i-1][j-1])%P;
        }
        (ans+=f[i][2])%=P;
    }
    printf("%d\n",(ans+P)%P);
}

signed main()
{
    int T;
    scanf("%d",&T);
    while(T--)
        solve();
    return 0;
}
posted @ 2026-11-04 14:14  ShanLiing  阅读(3)  评论(0)    收藏  举报
//雪花飘落效果