题目大意
在本题中,我们用 \(f_i\) 来表示第 \(i\) 个斐波那契数 \(f_1=f_2=1,f_i=f_{i-1}+f_{i-2}(i\ge 3)\)。
给定一个 \(n\) 个数的序列 \(a\)。
有 \(m\) 次操作,操作有两种:
-
将 \(a_l\sim a_r\) 加上 \(x\)。
-
求 \(\displaystyle\left(\sum_{i=l}^r f_{a_i}\right)\bmod (10^9+7)\)。
\(1\le n,m\le 10^5\),\(1\le a_i\le 10^9\)。
解题思路
两个操作,区间加,区间求和。
显然线段树。
斐波那契数怎么搞?
\(\mathcal O(n)\) 预处理显然爆。
显然用 \(\mathcal O(\log n)\) 的矩阵快速幂来求。
设 \(base=\begin{pmatrix}1&1\\1&0\end{pmatrix}\),则有 \(\begin{bmatrix} f_n && f_{n-1} \\ f_{n-1} && f_{n-2}\end{bmatrix}=\begin{pmatrix}1&0\\0&1\end{pmatrix} * base^{n-1}\)。
由于矩阵具有分配律,即 \(a\times b+a\times c=a\times(b+c)\),所以对于一段区间的矩阵可以相加维护,所以 \(\sum\limits_{i=l}^{r}f(a_i+x)=f_x*\sum\limits_{i=l}^{r}(a_i)\)。
线段树满足所有条件!!!
线段树上每个点都搞成一个矩阵即可。
具体请看代码。
CODE
#include <bits/stdc++.h>
#define int long long
inline int read()
{
int s = 0, f = 0;
char ch = getchar();
while (!isdigit(ch))
f |= (ch == '-'), ch = getchar();
while (isdigit(ch))
s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
return f ? -s : s;
}
using namespace std;
const int _ = 1e5 + 5;
const int INF = 1e9 + 7;
const int mod = 1e9 + 7;
struct Matrix
{
int a[3][3];
Matrix() { memset(a, false, sizeof a); }
void Init() { a[1][1] = a[2][2] = 1, a[1][2] = a[2][1] = 0; }
bool pd() { return a[1][1] == 1 && a[2][2] == 1 && a[1][2] == 0 && a[2][1] == 0; }
Matrix operator+(const Matrix b)
{
Matrix res;
for (int i = 1; i <= 2; ++i)
for (int j = 1; j <= 2; ++j)
res.a[i][j] = (a[i][j] + b.a[i][j]) % mod;
return res;
}
Matrix operator*(const Matrix b)
{
Matrix res;
for (int k = 1; k <= 2; ++k)
for (int i = 1; i <= 2; ++i)
for (int j = 1; j <= 2; ++j)
res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % mod;
return res;
}
Matrix operator^(int b)
{
Matrix res, Base;
for (int i = 1; i <= 2; ++i)
res.a[i][i] = 1;
for (int i = 1; i <= 2; ++i)
for (int j = 1; j <= 2; ++j)
Base.a[i][j] = a[i][j];
while (b)
{
if (b & 1)
res = res * Base;
Base = Base * Base;
b >>= 1;
}
return res;
}
} base, ans;
int n, m, a[_];
namespace Seg_tree
{
#define lson i << 1
#define rson i << 1 | 1
struct Tree
{
Matrix sum, lazy;
} tree[_ << 2];
void Push_up(int i) { tree[i].sum = tree[lson].sum + tree[rson].sum; }
void Build(int i, int l, int r)
{
tree[i].lazy.Init();
if (l == r)
{
if (a[l] == 1)
tree[i].sum.a[1][1] = 1;
else if (a[l] == 2)
tree[i].sum.a[1][1] = tree[i].sum.a[1][2] = 1;
else
tree[i].sum = ans * (base ^ (a[l] - 2));
return;
}
int mid = (l + r) >> 1;
Build(lson, l, mid), Build(rson, mid + 1, r);
Push_up(i);
}
void Push_down(int i)
{
if (tree[i].lazy.pd())
return;
tree[lson].lazy = tree[lson].lazy * tree[i].lazy;
tree[rson].lazy = tree[rson].lazy * tree[i].lazy;
tree[lson].sum = tree[lson].sum * tree[i].lazy;
tree[rson].sum = tree[rson].sum * tree[i].lazy;
tree[i].lazy.Init();
}
void Modify(int i, int l, int r, int L, int R, Matrix k)
{
if (L <= l && r <= R)
{
tree[i].lazy = tree[i].lazy * k, tree[i].sum = tree[i].sum * k;
return;
}
Push_down(i);
int mid = (l + r) >> 1;
if (mid >= L)
Modify(lson, l, mid, L, R, k);
if (mid < R)
Modify(rson, mid + 1, r, L, R, k);
Push_up(i);
}
int Query(int i, int l, int r, int L, int R)
{
if (L <= l && r <= R)
return tree[i].sum.a[1][1];
Push_down(i);
int mid = (l + r) >> 1, ans = 0;
if (mid >= L)
ans = (ans + Query(lson, l, mid, L, R)) % mod;
if (mid < R)
ans = (ans + Query(rson, mid + 1, r, L, R)) % mod;
return ans;
}
}
signed main()
{
base.a[1][1] = base.a[1][2] = base.a[2][1] = 1;
ans.a[1][1] = ans.a[1][2] = 1;
n = read(), m = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
Seg_tree::Build(1, 1, n);
for (int i = 1, opt, l, r, k; i <= m; ++i)
{
opt = read(), l = read(), r = read();
if (opt == 1)
{
k = read();
Seg_tree::Modify(1, 1, n, l, r, base ^ k);
}
else
{
printf("%lld\n", Seg_tree::Query(1, 1, n, l, r));
}
}
return 0;
}