传送门
这题是真的牛
- 区间加操作,使得区间的值加上对应的斐波那契数列。
- 区间查询操作,求区间和
其实这有个规律,看见斐波那契数列和(mod)是(1e9 + 9),就知道要用二次剩余来搞了,因为斐波那契的通项公式(sqrt{5})在模(1e9 + 9)下是有二次剩余的。
根据二次剩余,斐波那契的另一个表达式为
[F_n equiv 276601605(691504013^n-308495997^n) space (modspace 1e9+9)
]
那么就可以把这题转换为用线段树维护一下等比数列了。但是注意的是,这题是有两个等比数列,因为等比数列加等比数列不一定是等比数列,除非(q)相同。
那么维护两个等比数列,线段树里构建两个值用于维护当前的左区间的首项值,两个区间的公比是不同的。
注意的时,在每次修改都需要注意首项的值,因为操作里面首项(a_1)是对于给出的左区间而言的,所以对于每个线段区间,都需要处理好当前左区间的首项值。而且,在pushdown时也需要注意变化右孩子的首项值。
考虑到在线段树里面用快速幂会乘上一个(logn)的时间复杂度,所以就先预处理下两个公比的幂次方,等比数列求和时分母的逆元的值。
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int N = 3e5 + 5;
const int mod = 1e9 + 9;
const int q1 = 691504013, q2 = 308495997;
const int aa1 = 138300803, aa2 = 861699207;
const int inv1 = 691504013, inv2 = 308495997;
ll mul1[N] = {1}, mul2[N] = {1};
void ex_gcd(ll a, ll b, ll &d, ll &x, ll &y){
if(!b) {
d = a, x = 1, y = 0;
return;
}
ex_gcd(b, a % b, d, y, x);
y -= x * (a / b);
}
ll inv(ll a, ll p){//求a在p模下的逆元
ll d, x, y;
ex_gcd(a, p, d, x, y);
return d == 1 ? (x % p + p) % p : -1;//d = 1时才有逆元
}
ll S(ll n, ll a1, ll q){
if(q == q1) return (a1 * (mul1[n] - 1) % mod * inv1 % mod + mod) % mod;
return (a1 * (mul2[n] - 1) % mod * inv2 % mod + mod) % mod;
}
struct SegTree{
struct Node{
int l, r;
ll sum, a1, a2;
#define l(p) tree[p].l
#define r(p) tree[p].r
#define sum(p) tree[p].sum
#define a1(p) tree[p].a1
#define a2(p) tree[p].a2
#define lson (p << 1)
#define rson (p << 1 | 1)
} tree[N << 2];
void pushup(int p){
sum(p) = (sum(lson) + sum(rson) + mod) % mod;
}
void pushdown(int p) {
if(a1(p)) {
a1(lson) += a1(p); a1(lson) = (a1(lson) % mod + mod) % mod;
a1(rson) += mul1[l(rson) - l(lson)] * a1(p) % mod; a1(rson) = (a1(rson) % mod + mod) % mod;
sum(lson) += S(r(lson) - l(lson) + 1, a1(p), q1); sum(lson) = (sum(lson) % mod + mod) % mod;
sum(rson) += S(r(rson) - l(rson) + 1, mul1[l(rson) - l(lson)] * a1(p) % mod, q1); sum(rson) = (sum(rson) % mod + mod) % mod;
a1(p) = 0;
}
if(a2(p)) {
a2(lson) += a2(p); a2(lson) = (a2(lson) % mod + mod) % mod;
a2(rson) += mul2[l(rson) - l(lson)] * a2(p) % mod; a2(rson) = (a2(rson) % mod + mod) % mod;
sum(lson) += S(r(lson) - l(lson) + 1, a2(p), q2); sum(lson) = (sum(lson) % mod + mod) % mod;
sum(rson) += S(r(rson) - l(rson) + 1, mul2[l(rson) - l(lson)] * a2(p) % mod, q2); sum(rson) = (sum(rson) % mod + mod) % mod;
a2(p) = 0;
}
}
void build(int p, int l, int r){
l(p) = l, r(p) = r;
a1(p) = a2(p) = sum(p) = 0;
if(l == r) return;
int mid = (l + r) >> 1;
build(lson, l, mid); build(rson, mid + 1, r);
pushup(p);
}
void Change(int p, int l, int r, int c){//[l,r]
if(l <= l(p) && r(p) <= r){
if(c == q1) {
ll shou = 1ll * aa1 * mul1[l(p) - l] % mod; shou = (shou + mod) % mod;
a1(p) += shou, a1(p) = (a1(p) + mod) % mod;
sum(p) += S(r(p) - l(p) + 1, shou, q1), sum(p) = (sum(p) % mod + mod) % mod;
}
if(c == q2) {
ll shou = 1ll * aa2 * mul2[l(p) - l] % mod; shou = (shou + mod) % mod;
a2(p) += shou, a1(p) = (a1(p) + mod) % mod;
sum(p) += S(r(p) - l(p) + 1, shou, q2), sum(p) = (sum(p) % mod + mod) % mod;
}
return;
}
pushdown(p);
int mid = (l(p) + r(p)) >> 1;
if(l <= mid) Change(lson, l, r, c);
if(r > mid) Change(rson, l, r, c);
pushup(p);
}
ll Query(int p, int l, int r) { // [l,r]区间查询
if(l <= l(p) && r(p) <= r) {
return (sum(p) % mod + mod) % mod;
}
pushdown(p);
int mid = (l(p) + r(p)) >> 1;
ll ans = 0;
if(l <= mid) ans += Query(lson, l, r), ans = (ans % mod + mod) % mod;
if(r > mid) ans += Query(rson, l, r), ans = (ans % mod + mod) % mod;
return ans = (ans % mod + mod) % mod;;
}
} seg;
namespace IO {
template <typename T>
inline void w(T x) { if (x > 9) w(x / 10); putchar(x % 10 + 48); }
template <typename T>
inline void write(T x, char c) { if(x < 0) putchar('-'), x = -x; w(x); putchar(c); }
template <typename T>
inline void read(T &x) {
x = 0; T f = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
x *= f;
}
};
int n, m;
int a[N];
void subtask1(int l, int r){
seg.Change(1, l, r, q1);
seg.Change(1, l, r, q2);
}
void subtask2(int l, int r){
ll ans = a[r] - a[l - 1];
ans += seg.Query(1, l, r); ans = (ans % mod + mod) % mod;
IO::write(ans, '
');
}
int main(){
for(int i = 1; i <= N - 3; i++) {
mul1[i] = 1ll * mul1[i - 1] * q1 % mod;
mul2[i] = 1ll * mul2[i - 1] * q2 % mod;
}
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) IO::read(a[i]), a[i] = (a[i] + a[i - 1]) % mod;
seg.build(1, 1, n);
for(int i = 1; i <= m; i++) {
int op, l, r;
IO::read(op), IO::read(l), IO::read(r);
if(op == 1) subtask1(l, r);
else subtask2(l, r);
}
return 0;
}