题目大意
描述
维护一个n个数字的数列,最初所有的数字都是零,有三种操作:
1.将数列的第k个数字加d。
2.查询
ai
a
i
的和,其中
l≤i≤r
l
≤
i
≤
r
。
3.将
ai
a
i
更改为最近的Fibonacci数,其中
l≤i≤r
l
≤
i
≤
r
。
令
F0=1,F1=1
F
0
=
1
,
F
1
=
1
,对于
n≥2
n
≥
2
,斐波纳契数
Fn
F
n
定义为
Fn=Fn−1+Fn−2
F
n
=
F
n
−
1
+
F
n
−
2
。
数
x
x
的最近斐波那契数表示最小的,其中
|Fn−x|
|
F
n
−
x
|
也是最小的。
样例输入
1 1
2 1 1
5 4
1 1 7
1 3 17
3 2 4
2 1 5
样例输出
0
22
数据范围
1≤n≤100000,1≤m≤100000,|d|<231 1 ≤ n ≤ 100000 , 1 ≤ m ≤ 100000 , | d | < 2 31
解题思路
用线段树来维护这个序列。
我们发现,如果一个数已经被修改成了一个斐波那契数,再对其进行修改并不会影响它的值,所以我们可以对线段树每个节点打一个标记表示这个节点表示的区间是否都是斐波那契数,这样,每次区间修改时,如果标记为“是”就不用修改,只需对标记为“否”的节点进行修改。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
#define mid ((tr[id].l+tr[id].r)>>1)
using namespace std;
typedef long long LL;
const int N = 100005;
int n, m, opt, ql, qr, dex;
LL f[N];
struct seg_tree{
int l, r;
LL sum;
bool fib;
void init(){
l = r = 0;
sum = 0;
fib = false;
}
}tr[N<<2];
inline LL getFib(LL x){
if(x == 0) return 1;
int t = lower_bound(f, f+101, x) - f;
if(x - f[t-1] > f[t] - x) return f[t];
return f[t-1];
}
inline void pushup(int id){
tr[id].sum = tr[lid].sum + tr[rid].sum;
tr[id].fib = tr[lid].fib & tr[rid].fib;
}
void build(int id, int l, int r){
tr[id].init();
tr[id].l = l, tr[id].r = r;
if(tr[id].l == tr[id].r) return;
build(lid, l, mid);
build(rid, mid+1, r);
pushup(id);
}
void add(int id, int pos, LL val){
if(tr[id].l == tr[id].r){
tr[id].sum += val;
tr[id].fib = false;
return;
}
if(pos <= mid) add(lid, pos, val);
else add(rid, pos, val);
pushup(id);
}
LL query(int id, int l, int r){
if(tr[id].l == l && tr[id].r == r) return tr[id].sum;
if(r <= mid) return query(lid, l, r);
else if(l > mid) return query(rid, l, r);
else return query(lid, l, mid) + query(rid, mid+1, r);
}
void change(int id, int l, int r){
if(tr[id].l == tr[id].r){
tr[id].sum = getFib(tr[id].sum);
tr[id].fib = true;
return;
}
if(r <= mid) change(lid, l, r);
else if(l > mid) change(rid, l, r);
else{
if(!tr[lid].fib) change(lid, l, mid);
if(!tr[rid].fib) change(rid, mid+1, r);
}
pushup(id);
}
int main(){
f[0] = f[1] = 1;
for(int i = 2; i <= 100; i++) f[i] = f[i-1] + f[i-2];
while(scanf("%d%d", &n, &m)!=EOF){
build(1, 1, n);
while(m--){
scanf("%d%d%d", &opt, &ql, &qr);
if(opt == 1) add(1, ql, qr);
else if(opt == 2) printf("%lld\n", query(1, ql, qr));
else if(opt == 3) change(1, ql, qr);
}
}
return 0;
}