题目内容
塔子哥是一个程序员,他最近在处理一些字符串相关的任务。他喜欢 R
字符,因为在某些任务中,这个字符通常表示“正确”的结果。另一方面,他不喜欢 B
字符,因为在某些任务中,这个字符通常表示“错误”的结果。
为了解决他的任务,塔子哥定义了字符串的权值为字符串中 R
字符的出现次数。例如,对于字符串 BBRBRB
,它的权值为
2
2
2,因为其中有
2
2
2 个 R
字符。
现在,塔子哥面临一个问题,他有一个长度为
n
n
n 的字符串
s
s
s,它仅由 R
和 B
组成。他想知道,长度为
n
n
n 的仅由 R
和 B
组成的字符串中,字典序不小于
s
s
s 的字符串的权值之和是多少?因此,他需要编写一个程序来解决这个问题。
由于答案可能太大,需要对 1 0 9 + 7 10^9+7 109+7 取模后再输出。
输入描述
输入第一行为一个整数 n n n ,表示字符串的长度。
输入第二行为一个长度为
n
n
n 的字符串
s
s
s ,字符串中元素组成仅为 R
和 B
。
2 ≤ n ≤ 1 0 5 2\le n \le 10^5 2≤n≤105
输出描述
输出一个整数,代表长度为 n n n 的、字典序不小于 s s s 的字符串权值之和。
样例
输入
3
RBR
输出
7
样例解释
共有
3
3
3 个字符串符合要求:
RBR
的权值为
2
2
2 。
RRB
的权值为
2
2
2 。
RRR
的权值为
3
3
3 。
思路:数学题 or 数位dp
step1:转换题意
将 R R R 看作1, B B B看作0.原字符串变成一个二进制数 x x x.那么题目意思转化为:输出值域 [ x , 2 ∣ x ∣ − 1 ] [x,2^{|x|}-1] [x,2∣x∣−1] 内每个数的二进制位1的个数的总和。
step2:数学
一个直观的想法就是直接上数位dp。那么就是一道非常裸的数位dp。但是这里给出另一种做法:
由于 y ≥ x y \geq x y≥x , 那么从二进制位上来观察, y y y 和 x x x一定是有一段公共前缀(这个前缀可以为空)。并且在某一个位置 i i i , 满足 x i = 0 , y i = 1 x_i = 0 , y_i = 1 xi=0,yi=1 . 之后的 [ i + 1 , n ] [i+1,n] [i+1,n] 这些位置上,对 y y y 没有限制.
所以我们枚举这个"变化点" i ∈ [ 1 , n + 1 ] a n d x i = 0 i \in [1 , n + 1]\ and\ x_i = 0 i∈[1,n+1] and xi=0 ( i = n + 1 i=n+1 i=n+1代表 y = x y=x y=x) . 对于一个固定的 i i i , 答案就是 前缀中 1 1 1的个数 * 2 n − i − 1 2^{n-i-1} 2n−i−1 加上 [ 0 , 2 n − i − 1 − 1 ] [0,2^{n-i-1}-1] [0,2n−i−1−1] 中1的个数.
第一部分好算,前缀和一下,乘一下。第二部分考虑每一位的贡献也能够发现答案就是 2 n − i − 2 ∗ ( n − i − 1 ) 2^{n-i-2}*(n-i-1) 2n−i−2∗(n−i−1)。
代码
python
mod = 10**9 + 7
p2 = [1]
# 求[0,2^{x}-1] 中1的个数 , 公式如上
def cnt1 (x):
if x == 0:
return 0
return p2[x - 1] * x % mod
n = int(input())
a = list(input())
# 预处理2^i 在mod意义下的值
for i in range(n + 5):
p2.append(p2[-1] * 2 % mod)
# 将RB字符串 转化为 01字符串
b = [0] * n
for i in range(n):
if a[i] == 'R':
b[i] = 1
else:
b[i] = 0
# 求前缀中1的个数
p = [0]
for x in b:
p.append(p[-1] + x)
# 枚举 变化点 i
ans = p[-1]
for i in range(n):
if b[i] == 0:
# 答案的第二部分
ans = (ans + cnt1(n - i - 1)) % mod
# 答案的第一部分
ans = (ans + p2[n - i - 1] * (p[i + 1] + 1) % mod) % mod
print(ans)