测试地址:萃香的请柬
做法:本题需要用到找规律(划掉)数学证明。
首先我们要猜(划掉)观察出两个结论:
第一:若第一个字符串为L,第二个字符串为B,则以后任意字符串都是前一个字符串后面接上前一个字符串的前一个字符串,如第三个字符串为B+L=BL,第四个为BL+B=BLB。
第二:无限长时间后,序列和初始状态无关。
第一个结论的证明,我们用数学归纳法,对于前三个字符串显然满足这个结论,而对于第个字符串,若第个字符串为第和个字符串拼接而成,那么经过一个单位时间后,第个字符串演变为第个字符串,第个字符串演变为第个字符串,因此第个字符串就由第和个字符串拼接而成,得证。
而对于第二个结论,因为所有的过程都是像上面那样不断向后接,所以无限长的时间后序列的状态肯定是一定的。
根据上面的结论,我们知道对于任意一个字符串,其一定包含该字符串前面的每一个字符串,并且是作为前缀(除了L)。又根据上面的结论,我们知道一个这样的字符串的长度是一个斐波那契数,其中所含的B的数量也是斐波那契数。因此,我们计算答案的时候,用区间的答案减去的答案,而计算的答案的方法就是对其进行斐波那契数拆分,即从大到小试验斐波那契数,如果达到当前斐波那契数,则表示询问的前缀中包含一个长度为当前斐波那契数的像上面形式的字符串,那么我们就累加答案,然后因为该字符串后面一定也会接着比这个字符串长度更小的字符串,所以我们直接将减去当前的斐波那契数,继续试验即可。
据试验,最大需要用到的斐波那契数为第项(令第项均为),所以总的时间复杂度应该是,可以通过此题。
以下是本人代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
char s[1000010];
int n;
ll fib[110],l,r;
ll solve(ll x)
{
ll ans=0;
for(int i=92;i>=1;i--)
if (x>=fib[i]) x-=fib[i],ans+=fib[i-1];
return ans;
}
int main()
{
scanf("%s",s);
fib[0]=0,fib[1]=1;
for(int i=2;i<=92;i++)
fib[i]=fib[i-1]+fib[i-2];
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&l,&r);
ll ans=solve(r);
ans-=solve(l-1);
printf("%lld
",ans);
}
return 0;
}