AcWing 4078. 01串


01串

题意

给定一个整数 \(k\)
现在,我们可以对01字符串进行如下操作:
选择其中恰好 \(k\)连续\(1\) ,将它们全变成 \(0\)
如果一个 \(01\) 字符串可以通过若干次操作得到全 \(0\) 字符串,则称这个字符串为优秀的。
\(T\) 次询问,每次询问给出 \(l, r\) ,请问长度在 \([l, r]\) 区间的 \(01\) 优秀字符串的数量。

分析

组合数学问题,本来一直想着预处理每个长度字符串,连续 \(k\) 长度的 \(1\) 字符串的数量,再前缀和。后来看了题解发现根本不用。
这是一个计数dp问题(万物皆可dp \(\ldots\))。
\(f(i)\) 表示长度为 \(i\) 的 优秀字符串的数量。
对于最后一个字符,如果为 \(0\) ,那么 \(f(i) = f(i-1)\)
如果为 \(1\) ,那么后面连续 \(k\) 个字符串一定需要为 \(1\) ,则 \(f(i) = f(i-1) + f(i-k)\)

Code

#include 
using namespace std;

const int N = 100010, mod = 1e9 + 7;

int f[N]; // f(i) 表示长度为i时,优秀字符串的个数
int pre[N]; // 统计前缀和

int main ()
{
    int t, k; cin >> t >> k;
    f[0] = 1;
    for (int i = 1; i < N; i ++ )
    {
        f[i] = f[i-1];
        if (i >= k) f[i] = (f[i-1] + f[i-k]) % mod;
    }
    for (int i = 1; i < N; i ++ ) pre[i] = (pre[i-1] + f[i]) % mod;
    while(t -- )
    {
        int l, r; cin >> l >> r;
        cout << ((pre[r] - pre[l-1]) % mod + mod) % mod << endl;
    }
    return 0;
}
`