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 <iostream>
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;
}
`