思维题第二发…
804B. Minimum number of steps
Tags:数学 思维 快速幂
题目描述(bba?满脑子都是 PPO…)
给定一个仅由 a a a 和 b b b 组成的串。如果串中存在子串 a b ab ab,则将其置换成 b b a bba bba,持续到不含 a b ab ab 为止。
问一共置换了多少次( % 1 0 9 + 7 \%\ 10^9 + 7 % 109+7 )?
输入
仅一行。一个只含 a 、 b a、b a、b 的字符串。
长度范围: 1 ≤ l e n ≤ 1 0 6 1 \le len \le 10^6 1≤len≤106。
输出
一个整数,为置换次数( % 1 0 9 + 7 \%\ 10^9 + 7 % 109+7 )。
输入样例1
ab
输出样例
1
输入样例2
aab
输出样例
3
分析
咋回事?老老实实找规律吧。
会发现置换的顺序其实没有关系,但是每次置换都让 a a a 向右冒泡一格。
观察样例,再写个 a a a b aaab aaab 会发现得换 7 7 7 次,似乎就是 2 n − 1 2^n - 1 2n−1 ?
证明:
我们可以从左往右地对 a a a 进行替换。整个过程中我们总是让 a a a 聚在一起然后再一次性同时跨过某个 b b b。
这样对原串中的每个 b b b 都可以独立地进行分析并且不会重复计数(考察每个 b b b 的时候它的前面都紧挨着数个连续的 a a a)。
这样对于每个 b b b,如果它前面有 n a n_a na 个 a a a,就得有:
∑ i = 1 n a ( 1 2 i ) = = 1 × ( 1 − 2 n a ) 1 − 2 = = 2 n a − 1 \sum\limits_{i=1}^{n_a}(\frac{1}{2^i})\ ==\ \frac{1 \times (1-2^{n_a})}{1-2}\ ==\ 2^{n_a}-1 i=1∑na(2i1) == 1−21×(1−2na) == 2na−1
这么多次置换(相邻的 a a a 换 1 1 1 次,相间的 a a a 换 2 2 2 次,以此类推…)
思路:
这就很简单啦。把串遍历一遍,遇见 a a a 的时候进行计数,遇见 b b b 的时候把 2 n a − 1 2^{n_a}-1 2na−1 累加到 a n s ans ans 上即可。
(因为置换不改变 a a a 个数,所以不用模拟置换的全过程。轮到某个 b b b 时前面连续的 a a a 的个数就是原串中他前面的 a a a 的个数)
时间复杂度:
- 外层遍历, O ( n ) O(n) O(n) 的
- 内层做快速幂, O ( log n ) O(\log n) O(logn) 的
- 总时间复杂度 O ( n log n ) O(n\log n) O(nlogn) 的
AC代码
#include <stdio.h>
#define LL long long
#define PC putchar
template<typename T>void PRT(const T a){if(a<0){PC(45),PRT(-a);return;}if(a>=10)PRT(a/10);PC(a%10+48);}
template<typename T>void UPRT(const T a){if(a>=10)UPRT(a/10);PC(a%10+48);}
#define MOD 1000000007
LL QP(LL a, LL n){LL s=1;while(n){if(n&1)s=s*a%MOD;a=a*a%MOD;n>>=1;}return s;}
constexpr int ML(1e6+7);
char s[ML];
int main()
{
scanf("%s", s);
int cnt = 0;
LL ans = 0;
for (char *p=s; *p; ++p)
{
if (*p == 'a')
++cnt;
else
ans += QP(2, cnt) - 1, ans %= MOD;
}
UPRT(ans);
return 0;
}