无限序列

无限序列

YbtOj G. 3.无限序列

题目描述

我们按以下方式产生序列:

  1. 开始时序列是:1
  2. 每一次变化把序列中的 1 变成 100 变成 1

经过无限次变化,我们得到序列 1011010110110101101...

总共有Q个询问,每次询问为:在区间ab之间有多少个 1

任务:写一个程序回答Q个询问。

输入格式

输入的第一行为一个整数 ,后面有Q行,每行两个数用空格隔开的整数a,b

输出格式

输出共Q行,每行一个回答。

样例

样例输入

1
2 8

样例输出

4

数据范围与提示

对于100%的数据,1Q50001ab263

  • 做的时候先是手推到了变换8次后的结果试图找出循环节但是我失败了(

    然后发现了第i次变换后的序列的前一部分就是第i1次变换的序列

    然后就发现第i次变换后的序列的后一部分就是第i2次变换的序列

    但是没发现这好像可以继续拆下去

    然后发现了第i次变换后的序列中有个斐波那契数列第i项个1

    然后甚至意识到了[a,b]区间内的1的数量等于[1,b][1,a1]区间内的个数差

    然后我就不会了。。。

  • 看了一些题解,终于发现这个序列是可以不断拆分到10和1的

    //提前预处理出斐波那契数列1~99项的值
    //f函数用于寻找第1~m之间1的个数
    long long f(long long m){
    if(m < 3) return n1[m];
    //最好是从小往大找,因为这个序列是越拆越短的
    for(int i = 3;i<=99;i++){
    //如果长度正好等于预处理出的长度的第i项,那么1的个数也正好为对应的个数的第i项
    if(le[i] == m) return n1[i];
    //如果预处理出的长度第i项长于当前长度(即第一个长于长度的值),那么就接着拆后半部分
    //拆完后得到的1的数量加上前半部分的1的数量即可
    if(le[i] > m) return f(m-le[i-1]) + n1[i-1];
    }
    }

  • 举个栗子

    下面是变换8次后的序列
    1011 0101 1011 0101 1010 1101 1010 1101 10
    n1 = [1,1,2,3,5,8 ,13,...]
    le = [1,2,3,5,8,13,21,...]
    求在[3,18]区间内的1的数量
    先求[1,18]区间内的1的数量
    [1011 0101 1011 0101 10]10 1101 1010 1101 10
    最短的长于14的完整序列长度为21
    那么就可知最长的短于14的完整序列长为13
    [1011 0101 1011 0]101 1010 1101 1010 1101 10
    因此[1,18]区间内的1的数量为[1,13]与[14,18]之间的1的数量和
    通过查询可知,[1,13]区间内有8个1
    接下来将[14,18]拆分为[14,17]与[17,18]这两个完整序列
    即[14,18]间1的数量为以下两部分完整序列的和:
    [14,17]这个长为3的完整序列中的1的数量
    [17,18]这个长为2的完整序列中的1的数量
    因此,[14,18]间的1的数量为2+1=3个
    因此[1,18]区间内有8+3=11个1
    同理可知[1,2]区间内有1个1
    所以[3,18]间有11-1=10个1

还有就是

long long

posted @   Burnling  阅读(476)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示