1. 问题描述:
给定一个整数 k。现在,我们可以对 01 字符串进行如下操作:选择其中恰好 k 个连续的 1,将它们都变为 0。如果一个 01 字符串可以通过若干次上述操作,变为一个全 0 字符串,那么就称这个字符串很优秀。本题共需要回答 T 组询问,每组询问给定两个整数 l,r,并请你计算长度在 [l,r] 范围内的所有 01 字符串中优秀字符串的数量。
输入格式
第一行包含两个整数 T 和 k。接下来 T 行,每行包含两个整数 l,r。
输出格式
共 T 行,第 i 行输出第 i 组询问的答案对 10 ^ 9 + 7 取模后的结果。
数据范围
前三个测试点满足 1 ≤ T,k ≤ 10。
所有测试点满足 1 ≤ T,k ≤ 10 ^ 5,1 ≤ l ≤ r ≤ 10 ^ 5。
输入样例:
3 2
1 3
2 3
4 4
输出样例:
6
5
5
来源:https://www.acwing.com/problem/content/description/4081/
2. 思路分析:
分析题目可以知道我们需要求解区间[l,r]范围内所有 01 字符串中优秀字符串的数量,因为求解的是一个区间的值所以可以先预处理出前缀和s,s[i]表示长度小于等于i的优秀字符串的数量,这样s[r] - s[l - 1]就是答案,这样问题就转化为了如何求解每一个s[i],我们可以预处理一个f数组,其中f[i]表示长度为i的优秀字符串的数量,通过求解f[i]的前缀和最终我们就可以得到每一个s[i],所以问题就转化为了如何递推求出f列表的值,由于是一个递推的过程,f的状态表示已经确定了,f[i]表示长度为i的优秀字符串的数量,如何进行状态计算呢?状态计算一般对应集合的划分,一般是找最后一个不同点,我们可以根据当前枚举的第i位是0还是1将当前的f[i]划分为两个集合,如下图所示,当最后一位为0是时候是无法进行操作的,所以等于f(i - 1),如果最后一位为1那么将最后包含第i位连续的k个1变为0,所以与i - k的串是一一对应的,所以f(i) = f(i - 1) + f(i - k),如何确定初始状态呢?我们可以通过赋值的方法判断当前f(0)应该取什么值可以使得状态计算表达式是正确的,可以发现当f(0) = 1的时候状态计算表达式是正确的,最后计算一下f数组的前缀和那么就可以得到s,对于每一个询问[l,r],答案为s[r] - s[l - 1]。
3. 代码如下:
class Solution:
# 前缀和 + dp
def process(self):
N, mod = 100010, 10 ** 9 + 7
T, k = map(int, input().split())
f = [0] * N
f[0] = 1
# 预处理f列表
for i in range(1, N):
f[i] = f[i - 1]
# 只有当i >= k的时候状态i - k才是存在的
if i >= k:
f[i] = (f[i] + f[i - k]) % mod
for i in range(1, N):
# 直接将前缀和累加到f中
f[i] = (f[i] + f[i - 1]) % mod
for c in range(T):
l, r = map(int, input().split())
print((f[r] - f[l - 1]) % mod)
if __name__ == '__main__':
Solution().process()