原题链接:C. DZY Loves Fibonacci Numbers
题目大意:
定义斐波那契数列: F 1 = 1 , F 2 = 1 ; F n = F n − 1 + F n − 2 ( n > 2 ) F_{1}=1,F_{2}=1;F_{n}=F_{n-1}+F_{n-2}(n>2) F1=1,F2=1;Fn=Fn−1+Fn−2(n>2)。
现在给出一个长度为 n n n 正整数数组 a a a ,此外还有 m m m 次查询:
- 1 1 1 l l l r r r 表示对区间 [ l , r ] [l,r] [l,r] 内的所有 a i a_{i} ai 加上 F i − l + 1 F_{i-l+1} Fi−l+1 。
- 2 2 2 l l l r r r 表示对数组 a a a 在 [ l , r ] [l,r] [l,r] 之内的所有元素求和 ( ∑ i = l r a i ) (\sum_{i=l}^{r}a_{i}) (∑i=lrai) 。
对于每个询问 2 2 2 ,输出答案对 1 0 9 + 7 10^{9}+7 109+7 取模后的结果 。
解题思路:
首先看起来就非常像线段树的题。
对操作二的处理非常方便,我们只需要子节点的区间值向上合并即可。
但是对操作一的下传处理很不好做。
对区间 [ l , r ] [l,r] [l,r] 内的所有 a i a_{i} ai 加上 F i − l + 1 F_{i-l+1} Fi−l+1 ,这玩意有 i i i 和 l l l 的 影响,要是能把它们分别拆出来就好了。
首先注意到斐波那契有一个性质: F x + y = F x + 1 F y + F x F y − 1 F_{x+y}=F_{x+1}F_{y}+F_{x}F_{y-1} Fx+y=Fx+1Fy+FxFy−1
那么对于这一题而言: F i − l + 1 = F i + 1 F − l + 1 + F i F − l F_{i-l+1}=F_{i+1}F_{-l+1}+F_{i}F_{-l} Fi−l+1=Fi+1F−l+1+FiF−l
这样,我们就把 i i i 和 − l + 1 -l+1 −l+1 分开了。
那么原题的操作 1 1 1 就变成了:
F l − l + 1 + F ( l + 1 ) − l + 1 + . . . + F r − l + 1 = ( F l + 1 + F l + 2 + . . . + F r + 1 ) F − l + 1 + ( F l + F l + 1 + . . . + F r ) F − l F_{l-l+1}+F_{(l+1)-l+1}+...+F_{r-l+1}=(F_{l+1}+F_{l+2}+...+F_{r+1})F_{-l+1}+(F_{l}+F_{l+1}+...+F_{r})F_{-l} Fl−l+1+F(l+1)−l+1+...+Fr−l+1=(Fl+1+Fl+2+...+Fr+1)F−l+1+(Fl+Fl+1+...+Fr)F−l
这样,操作 1 1 1 的标记下传和懒标记合并就可以直接做了。
但是斐波那契数列没有负数下标啊?
没关系,就像 F 3 = F 2 + F 1 F_{3}=F_{2}+F_{1} F3=F2+F1 一样,我们也可得 F 1 = F 3 − F 2 F_{1}=F_{3}-F_{2} F1=F3−F2,这是满足的。
所以 F 0 = F 2 − F 1 , F − 1 = F 1 − F 0 , . . . , F − n = F − n + 2 − F − n + 1 F_{0}=F_{2}-F_{1},F_{-1}=F_{1}-F_{0},...,F_{-n}=F_{-n+2}-F_{-n+1} F0=F2−F1,F−1=F1−F0,...,F−n=F−n+2−F−n+1
我们将负数下标的斐波那契预处理出来,再处理原斐波那契的前缀和就好了。
具体而言:线段树维护区间和 s u m sum sum,再维护两个标记 a 1 , a 2 a1,a2 a1,a2 。
区间标记下推时:
s
u
m
=
s
u
m
+
(
p
r
e
f
i
x
F
r
+
1
−
p
r
e
f
i
x
F
l
)
∗
a
1
′
+
(
p
r
e
f
i
x
F
r
−
p
r
e
f
i
x
F
l
−
1
)
∗
a
2
′
sum = sum + (prefixF_{r+1}-prefixF_{l})*a_1'+(prefixF_{r}-prefixF_{l-1})*a_2'
sum=sum+(prefixFr+1−prefixFl)∗a1′+(prefixFr−prefixFl−1)∗a2′
a
1
=
a
1
+
a
1
′
a_{1}=a_{1}+a_{1}'
a1=a1+a1′
a
2
=
a
2
+
a
2
′
a_{2}=a_{2}+a_{2}'
a2=a2+a2′
区间信息合并时:
s u m ′ = s u m l s o n + s u m r s o n sum'=sum_{lson}+sum_{rson} sum′=sumlson+sumrson
其中
a
1
′
,
a
2
′
a_{1}',a_{2}'
a1′,a2′ 为父亲节点下传的标记值,即所有未下传操作中
F
−
l
+
1
,
F
−
l
F_{-l+1},F_{-l}
F−l+1,F−l 的和,
s
u
m
′
sum'
sum′ 为父亲的区间和,
s
u
m
l
s
o
n
,
s
u
m
r
s
o
n
sum_{lson},sum_{rson}
sumlson,sumrson 分别为左右儿子的区间和。(记得取模)
时间复杂度: O ( n log n ) O(n \log n) O(nlogn)
AC代码:
#include <bits/stdc++.h>
using namespace std;
using PII = pair<int, int>;
using i64 = long long;
//线段树板子
template<class Info, class Tag>
struct Segtree {
#define lson k << 1, l, mid
#define rson k << 1 | 1, mid + 1, r
int n;
vector<Info> info;
vector<Tag> tag;
Segtree(int _n) : n(_n), info((_n + 5) << 2), tag((_n + 5) << 2) {};
Segtree(vector<Info>& arr) : Segtree(arr.size() - 1) {
function<void(int, int, int)> build = [&](int k, int l, int r) {
if (l == r) {
info[k] = arr[l];
return;
}
int mid = l + r >> 1;
build(lson), build(rson);
pushup(k);
};
build(1, 1, n);
}
void pushdown(int k, int l, int r) {
int mid = l + r >> 1, lt = k << 1, rt = k << 1 | 1;
info[lt].down(tag[k], l, mid);
info[rt].down(tag[k], mid + 1, r);
tag[lt].down(tag[k]);
tag[rt].down(tag[k]);
tag[k] = Tag();//初始化tag
}
void pushup(int k) {
info[k] = merge(info[k << 1], info[k << 1 | 1]);
}
void Modify(int k, int l, int r, int x, int y, const Tag& z) {
if (l >= x && r <= y) {
info[k].down(z, l, r);
tag[k].down(z);
return;
}
if (tag[k].change()) pushdown(k, l, r);
int mid = l + r >> 1;
if (x <= mid) Modify(lson, x, y, z);
if (y > mid) Modify(rson, x, y, z);
pushup(k);
}
Info Query(int k, int l, int r, int x, int y) {
if (l >= x && r <= y) return info[k];
if (tag[k].change()) pushdown(k, l, r);
int mid = l + r >> 1;
if (y <= mid) return Query(lson, x, y);
if (x > mid) return Query(rson, x, y);
return merge(Query(lson, x, y), Query(rson, x, y));
}
void Modify(int l, int r, const Tag& z) {
Modify(1, 1, n, l, r, z);
}
Info Query(int l, int r) {
return Query(1, 1, n, l, r);
}
};
const int mod = 1e9 + 9, N = 3e5 + 10;
i64 fib[N], rfib[N], pfib[N];
//Tag内的 down 为标记之间的合并 t为父亲下传的标记
struct Tag {
i64 a1{}, a2{};
bool change() { return a1 || a2; }
void down(const Tag& t) {
a1 = (a1 + t.a1) % mod;
a2 = (a2 + t.a2) % mod;
}
};
//Info内的 down 为父亲标记对子节点的下传 t为父亲下传的标记
//Info内的 merge 为父亲节点对儿子节点信息的合并 a为左儿子 b为右儿子
struct Info {
i64 sum;
void down(const Tag& t, int l, int r) {
sum = (sum + (pfib[r + 1] - pfib[l] + mod) % mod * t.a1 % mod) % mod;
sum = (sum + (pfib[r] - pfib[l - 1] + mod) % mod * t.a2 % mod) % mod;
}
friend Info merge(const Info& a, const Info& b) {
Info res;
res.sum = (a.sum + b.sum) % mod;
return res;
}
};
void solve() {
int n, m;
cin >> n >> m;
vector<Info> a(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i].sum;
}
Segtree<Info, Tag> Seg(a);
int op, l, r;
for (int i = 1; i <= m; ++i) {
cin >> op >> l >> r;
if (op == 1) {
// a1 为 F[-l+1] / a2 为 F[-l]
Seg.Modify(l, r, { rfib[l - 1], rfib[l] });
} else {
cout << Seg.Query(l, r).sum << '\n';
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
fib[1] = fib[2] = 1;//斐波那契数列
pfib[1] = 1, pfib[2] = 2;//前缀和斐波那契数列
rfib[0] = 0, rfib[1] = 1, rfib[2] = mod - 1;//斐波那契数列负下标
for (int i = 3; i < N; ++i) {
fib[i] = (fib[i - 1] + fib[i - 2]) % mod;
pfib[i] = (pfib[i - 1] + fib[i]) % mod;
rfib[i] = (rfib[i - 2] - rfib[i - 1] + mod) % mod;
}
int t = 1; //cin >> t;
while (t--) solve();
return 0;
}