题目描述

卡德加喜欢养兔子。他在达拉然的下水道里放了 $N$ 个兔笼(编号从 $1$ 到 $N$),里面养着他从德拉诺带来的兔子。它们的繁殖遵循斐波那契数列的规律:刚开始时,笼子里有一对刚出生的兔子。每对兔子在出生第二个月后,每个月都生一对兔子。(第一个月结束后有 $1$ 对兔子。第二个月结束后有 $2$ 对。)

卡德加从苏拉玛的的大魔导师艾利桑德那边学习了先进的扭曲时空法术。有时候,他会对一排连续的兔笼(从第 $L$ 号到第 $R$ 号)释放时光流逝法术,让这些兔笼里的时间前进 $K$ 个月。另外一些时候,他想喂一下兔子,所以他想知道第 $L$ 号到第 $R$ 号兔笼里有多少只兔子。

(假设这些操作都是在一个月以内完成的,不需要考虑自然时间对兔子的影响。)

输入

第一行两个整数 $N,M$, 表示兔笼的数量和操作的数量。

接下来 $M$ 行,每行包含三个数 $L,R,K$。如果 $K > 0$,说明卡德加在使用时光流逝,编号 $L$ 到 $R$ 的兔笼时间前进 $K$ 个月。如果 $K = 0$,说明他只是想喂兔子了,输出这些兔笼里有多少兔子。

输出

对每个喂兔子的操作,输出兔子的数量。答案模 $10007$。

样例输入

10 10
1 3 2
1 1 0
2 4 0
3 5 0
4 7 3
3 5 0
1 4 0
2 7 0
1 9 4
2 10 0

样例输出

2
5
4
8
9
16
121

来源

2017 年 NOIP 夏令营

看到了询问区间,马上想到线段树;看到了斐波那契数列,马上想到矩阵快速幂。问题在于怎么结合了。

对于两个数列,如果这两个数列都具有斐波那契性质,则这两个数列的和也具有斐波那契性质。

什么意思呢?就是说对于两个数列 $\{x_1,x_2,x_3,x_4,\dots\}$,$\{y_1,y_2,y_3,y_4,\dots\}$,如果 $x_1+x_2=x_3, x_2+x_3=x_4, \dots, y_1+y_2=y_3, y_2+y_3=y_4, \dots$

如果有一个数列 $\{z_1,z_2,z_3,z_4,\dots\}$,其中 $z_1=x_1+y_1, z_2=x_2+y_2, \dots, z_k=x_k+y_k, \dots$

则有 $z_1+z_2=z_3, z_2+z_3=z_4, \dots$

这个不难证明,利用等式的性质就好了。

利用这个特性,我们可以在线段树的每个地方存储运算时的矩阵(因为满足分配率)。更新的时候,用矩阵加法就可以了。

另外,由 $\text{BSGS}$ 算法可知,此题还有一个优化:

斐波那契数列对 $10007$ 取模时,第 $1$ 个数与第 $20017$ 个数是一样的,第 $2$ 个数与第 $20018$ 个数是一样的。

这样就可以预先算好 $20017$ 个矩阵的值了,不需要使用快速幂。

代码如下:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 using namespace std;
  5
  6 struct Matrix {
  7   int mat[2][2];
  8   Matrix() { memset(mat, 0, sizeof mat); }
  9 };
 10
 11 const int MaxN = 100000 + 5;
 12 const int Mod = 10007;
 13
 14 int N, M;
 15 Matrix One, Fibo, Zero;
 16 int L[MaxN * 4], R[MaxN * 4];
 17 Matrix Sum[MaxN * 4], Tag[MaxN * 4];
 18
 19 inline Matrix operator + (Matrix A, Matrix B) {
 20   Matrix C;
 21   for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j)
 22     C.mat[i][j] = (A.mat[i][j] + B.mat[i][j]) % Mod;
 23   return C;
 24 }
 25
 26 inline Matrix operator * (Matrix A, Matrix B) {
 27   Matrix C;
 28   for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j) {
 29     for (int k = 0; k < 2; ++k) C.mat[i][j] += A.mat[i][k] * B.mat[k][j] % Mod;
 30     C.mat[i][j] %= Mod;
 31   }
 32   return C;
 33 }
 34
 35 inline Matrix operator ^ (Matrix low, int high) {
 36   Matrix ans;
 37   ans.mat[0][0] = ans.mat[1][1] = 1;
 38   while (high) {
 39     if (high & 1) ans = ans * low;
 40     high >>= 1;
 41     low = low * low;
 42   }
 43   return ans;
 44 }
 45
 46 inline int read() {
 47   int x = 0; char c;
 48   do c = getchar(); while (c < '0' || c > '9');
 49   do x = (x << 1) + (x << 3) + c - '0', c = getchar(); while (c >= '0' && c <= '9');
 50   return x;
 51 }
 52
 53 inline void Push_up(int i) { Sum[i] = Sum[i << 1] + Sum[i << 1 | 1]; }
 54
 55 inline void Push_down(int i) {
 56   int lson = i << 1, rson = i << 1 | 1;
 57
 58   Tag[lson] = Tag[lson] * Tag[i];
 59   Sum[lson] = Sum[lson] * Tag[i];
 60   Tag[rson] = Tag[rson] * Tag[i];
 61   Sum[rson] = Sum[rson] * Tag[i];
 62   Tag[i] = One;
 63 }
 64
 65 void Build_Tree(int l, int r, int i) {
 66   L[i] = l, R[i] = r;
 67   Tag[i] = One;
 68   if (l == r) {
 69     Sum[i].mat[0][0] = Sum[i].mat[0][1] = 1;
 70     return;
 71   }
 72   int mid = L[i] + R[i] >> 1;
 73   Build_Tree(l, mid, i << 1);
 74   Build_Tree(mid + 1, r, i << 1 | 1);
 75   Push_up(i);
 76 }
 77
 78 void Update_Tree(int l, int r, int k, int i) {
 79   if (L[i] == l && R[i] == r) {
 80     Sum[i] = Sum[i] * (Fibo ^ k);
 81     Tag[i] = Tag[i] * (Fibo ^ k);
 82     return;
 83   }
 84   Push_down(i);
 85
 86   int mid = L[i] + R[i] >> 1;
 87   if (r <= mid) Update_Tree(l, r, k, i << 1);
 88   else if (l > mid) Update_Tree(l, r, k, i << 1 | 1);
 89   else {
 90     Update_Tree(l, mid, k, i << 1);
 91     Update_Tree(mid + 1, r, k, i << 1 | 1);
 92   }
 93   Push_up(i);
 94 }
 95
 96 Matrix Query_Tree(int l, int r, int i) {
 97   if (L[i] == l && R[i] == r)
 98     return Sum[i];
 99   Push_down(i);
100
101   int mid = L[i] + R[i] >> 1;
102   if (r <= mid) return Query_Tree(l, r, i << 1);
103   else if (l > mid) return Query_Tree(l, r, i << 1 | 1);
104   else {
105     Matrix lc = Query_Tree(l, mid, i << 1), rc = Query_Tree(mid + 1, r, i << 1 | 1);
106     return lc + rc;
107   }
108 }
109
110 int main() {
111   N = read(); M = read();
112   One.mat[0][0] = One.mat[1][1] = 1;
113   Fibo.mat[0][1] = Fibo.mat[1][0] = Fibo.mat[1][1] = 1;
114   Build_Tree(1, N, 1);
115
116   Matrix res;
117   for (int m = 1; m <= M; ++m) {
118     int l, r, k;
119     l = read(); r = read(); k = read();
120     if (k == 0) {
121       res = Query_Tree(l, r, 1);
122       printf("%d\n", res.mat[0][0]);
123     } else
124       Update_Tree(l, r, k, 1);
125   }
126
127   return 0;
128 }

View Code

转载于:https://www.cnblogs.com/tweetuzki/p/8215487.html

FZOJ P2109 【卡德加的兔子】相关推荐

  1. 【9018题解】2109 卡德加的兔子

    题目描述 卡德加有N个兔子笼,编号从1~N.刚开始每个兔子笼里有1对小兔,每个月小兔会长成大兔,之后的每个月这对大兔都会生出1对小兔.即兔子的繁殖遵循斐波那契数列的规律. 例如:第一个月1对小兔,第二 ...

  2. 2017福建夏令营Day7(数论)

    埃匹希斯水晶 (apexis) ⼤家都知道,卡德加是⼀个神奇的法师. 有⼀天,他发现了⼀种可以作⽤在埃匹希斯⽔晶上的魔法:在左右两个 祭坛上放⼀定量的⽔晶,然后施放⼀个法术,左边⼀堆的⽔晶数量会变成原 ...

  3. 复杂问题的简单抽象:魔兽世界中的兔子们

    无论是复杂精妙的 BOSS 战,还是丰富多样的任务系统,<魔兽世界>都让当时的玩家大开眼界,但<魔兽世界>里有不少新颖的游戏机制,都建立在一个出人意料的东西上面--那就是兔子. ...

  4. 兔子生兔子递归的理解

    重要的是找规律! 古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 月份 兔子对数 1 1 2 1 3 2 ...

  5. Fib(兔子问题)python实现多种方法

    # 斐波那契数列是学计算机入门最经典的一道题目# 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci) # 以 ...

  6. 杨元庆:兔子要变成狼靠的是体制的激励

    近日,兔子和狼的故事刷遍了朋友圈.企业管理层和产经评论者,旗帜鲜明地纷纷站队表明立场.人力资源管理真的要把员工培养成狼才算成功吗?"企业是做长了或者做大了后就会有大企业病,不能光靠改变文化来 ...

  7. Rosalind: 兔子与递归

    问题描述 序列 指的是一组对象的集合,其中允许重复.序列分为有限序列和无限序列两种类型,我们通常用 表示序列中的第n个对象. 递归其实就是当前的序列依赖于之前的序列.最典型的案例就是兔子繁衍问题,假设 ...

  8. C语言解决关于兔子的古典问题的代码

    把做工程过程经常用的一些代码段做个收藏,如下的代码是关于C语言解决关于兔子的古典问题的代码,希望对各位朋友有一些好处. #include "stdio.h" #include &q ...

  9. BZOJ1001[BeiJing2006]狼抓兔子——最小割

    题目描述 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的, 而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形: ...

  10. 从“兔子狮子谁做老板”的故事,看企业管理

    一天,一只兔子在山洞前写文章,一只狼走了过来,问:"兔子啊,你在干什么?"答曰:"写文章."问:"什么题目?"答曰:"<浅谈 ...

最新文章

  1. POJ - 2763 Housewife Wind LCA+dfs序+线段树
  2. 每日一皮:学PHP的不容易...
  3. [partial] C#里partial关键字的作用
  4. STM32外设有哪些?外设在总线上是怎么挂载的?
  5. flex布局:子子元素过大撑开了设定flex:1的子元素的解决方案
  6. Keepalived实现LVS的高可用全解析
  7. 1.20 Java8新特性:Effectively final
  8. 全球及中国胶合板行业产量规模与营运能力研究报告2022版
  9. kindle 3快捷键
  10. 求2个数的最大公约数
  11. 求一个数的阶乘值c语言代码,求10000的阶乘(c语言代码实现)
  12. vue+node全栈移动商城【10】注册页面传值到node中间件
  13. 解决ajax中ie缓存问题(手动添加时间戳)
  14. compile error
  15. 14个非常棒的 JavaScript 游戏开发框架推荐
  16. 除法求模中求逆元的两种方法
  17. 7.Swoole的自定义协议功能的使用
  18. 内部类之.this.new
  19. HTTP并发测试工具
  20. js之好玩的特效黑洞粒子效果[1]

热门文章

  1. JS实现鼠标点击出现文字特效
  2. 线性拟合1-最小二乘法
  3. 网易服务器维护,网易:方便玩家 各大区服务器维护详细时间表列
  4. Ubuntu下通过命令打开图片
  5. mac ps安装 服务器无响应,Mac程序无响应?六个方法教你如何退出无响应的程序...
  6. 看完这个故事终于知道区块链是什么了
  7. 更换ip地址后虚拟机无法联网,连接失败
  8. 解决安装 Bun 之后出现 zsh compinit: insecure directories, run compaudit for list. Ignore insecure directorie
  9. Python通过IMAP实现邮箱客户端
  10. imap java代码,JAVA运用IMAP、POP3、SMTP协议收发邮件