题目描述
卡德加喜欢养兔子。他在达拉然的下水道里放了 $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$ 个矩阵的值了,不需要使用快速幂。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; struct Matrix {
int mat[][];
Matrix() { memset(mat, , sizeof mat); }
}; const int MaxN = + ;
const int Mod = ; int N, M;
Matrix One, Fibo, Zero;
int L[MaxN * ], R[MaxN * ];
Matrix Sum[MaxN * ], Tag[MaxN * ]; inline Matrix operator + (Matrix A, Matrix B) {
Matrix C;
for (int i = ; i < ; ++i) for (int j = ; j < ; ++j)
C.mat[i][j] = (A.mat[i][j] + B.mat[i][j]) % Mod;
return C;
} inline Matrix operator * (Matrix A, Matrix B) {
Matrix C;
for (int i = ; i < ; ++i) for (int j = ; j < ; ++j) {
for (int k = ; k < ; ++k) C.mat[i][j] += A.mat[i][k] * B.mat[k][j] % Mod;
C.mat[i][j] %= Mod;
}
return C;
} inline Matrix operator ^ (Matrix low, int high) {
Matrix ans;
ans.mat[][] = ans.mat[][] = ;
while (high) {
if (high & ) ans = ans * low;
high >>= ;
low = low * low;
}
return ans;
} inline int read() {
int x = ; char c;
do c = getchar(); while (c < '' || c > '');
do x = (x << ) + (x << ) + c - '', c = getchar(); while (c >= '' && c <= '');
return x;
} inline void Push_up(int i) { Sum[i] = Sum[i << ] + Sum[i << | ]; } inline void Push_down(int i) {
int lson = i << , rson = i << | ; Tag[lson] = Tag[lson] * Tag[i];
Sum[lson] = Sum[lson] * Tag[i];
Tag[rson] = Tag[rson] * Tag[i];
Sum[rson] = Sum[rson] * Tag[i];
Tag[i] = One;
} void Build_Tree(int l, int r, int i) {
L[i] = l, R[i] = r;
Tag[i] = One;
if (l == r) {
Sum[i].mat[][] = Sum[i].mat[][] = ;
return;
}
int mid = L[i] + R[i] >> ;
Build_Tree(l, mid, i << );
Build_Tree(mid + , r, i << | );
Push_up(i);
} void Update_Tree(int l, int r, int k, int i) {
if (L[i] == l && R[i] == r) {
Sum[i] = Sum[i] * (Fibo ^ k);
Tag[i] = Tag[i] * (Fibo ^ k);
return;
}
Push_down(i); int mid = L[i] + R[i] >> ;
if (r <= mid) Update_Tree(l, r, k, i << );
else if (l > mid) Update_Tree(l, r, k, i << | );
else {
Update_Tree(l, mid, k, i << );
Update_Tree(mid + , r, k, i << | );
}
Push_up(i);
} Matrix Query_Tree(int l, int r, int i) {
if (L[i] == l && R[i] == r)
return Sum[i];
Push_down(i); int mid = L[i] + R[i] >> ;
if (r <= mid) return Query_Tree(l, r, i << );
else if (l > mid) return Query_Tree(l, r, i << | );
else {
Matrix lc = Query_Tree(l, mid, i << ), rc = Query_Tree(mid + , r, i << | );
return lc + rc;
}
} int main() {
N = read(); M = read();
One.mat[][] = One.mat[][] = ;
Fibo.mat[][] = Fibo.mat[][] = Fibo.mat[][] = ;
Build_Tree(, N, ); Matrix res;
for (int m = ; m <= M; ++m) {
int l, r, k;
l = read(); r = read(); k = read();
if (k == ) {
res = Query_Tree(l, r, );
printf("%d\n", res.mat[][]);
} else
Update_Tree(l, r, k, );
} return ;
}