题意:一个序列n个数初始为0,m次操作。操作1,把第k个加上d;操作2,对l到r的结点求和;操作3,把l到r的结点变成最近的斐波那契数,如果到左右的斐波数相同就选小的那个。
这题真是坑了好久了啊。
第一次用的树状数组做,因为是单点增减区间求和,而变成斐波那契数的操作显然必须对叶子结点操作,然后更新上去,超时果断的。
其实如果一段区间之前做了3操作,而之后这个区间没有做过1操作,再做3操作是可以直接忽略的,用线段树在这里打lazy标记,每次3操作就打上lazy,1操作就抹去lazy。
这样一想其实是个模版题。
具体看代码。
//请用G++
#include<cstdio>
#include<cmath>
#include<algorithm>
#define LL __int64
#define lld I64d
using namespace std;
const int MN=100005;
LL fib[100]={1,1,2};
struct node{
int l,r,m;
bool lazy; //lazy表示当前结点的区间是否全部在进行过3操作之后没有1操作
LL val;
}tree[MN<<2];
int n,m;
void cfib(LL &val){ //二分
int p=lower_bound(fib,fib+100,val) -fib;
if(val-fib[p-1] > fib[p]-val) val=fib[p];
else val=fib[p-1];
}
void build(int c,int l,int r){
tree[c].l=l,tree[c].r=r,tree[c].lazy=0,tree[c].val=0;
if(l==r) return;
tree[c].m=(l+r)>>1;
build(c<<1,l,tree[c].m);
build(c<<1|1,tree[c].m+1,r);
}
void update(int c,int pos,int val){
tree[c].lazy=0; //抹去更新路径的lazy
if(tree[c].l==tree[c].r){
tree[c].val+=val;
return;
}
if(pos<=tree[c].m) update(c<<1,pos,val);
else update(c<<1|1,pos,val);
tree[c].val=tree[c<<1].val+tree[c<<1|1].val;
tree[c].lazy=tree[c<<1].lazy&tree[c<<1|1].lazy;
}
void fibdown(int c){
if(tree[c].lazy) return; //lazy标记过,当前结点的数已经是斐波数了
tree[c].lazy=1; // 打上lazy
if(tree[c].l==tree[c].r){
if(!tree[c].val) tree[c].val=1;
else cfib(tree[c].val);
return;
}
fibdown(c<<1);
fibdown(c<<1|1);
tree[c].val=tree[c<<1].val+tree[c<<1|1].val;
}
void FIB(int c,int ql,int qr){
if(tree[c].lazy) return;
if(tree[c].l>=ql&&tree[c].r<=qr){
fibdown(c); //这棵子树被完全覆盖,用fibdown函数更新这棵子树的叶子节点
return;
}
if(ql<=tree[c].m) FIB(c<<1,ql,qr);
if(qr>tree[c].m) FIB(c<<1|1,ql,qr);
tree[c].val=tree[c<<1].val+tree[c<<1|1].val;
tree[c].lazy=tree[c<<1].lazy&tree[c<<1|1].lazy; //当前结点的lazy右左右儿子的lazy决定
}
LL query(int c,int ql,int qr){
if(tree[c].l>=ql&&tree[c].r<=qr){
return tree[c].val;
}
LL res=0;
if(ql<=tree[c].m) res+=query(c<<1,ql,qr);
if(qr>tree[c].m) res+=query(c<<1|1,ql,qr);
return res;
}
int main(){
int a,b,c;
for(int i=3;i<100;++i) fib[i]=fib[i-1]+fib[i-2];
while(scanf("%d %d",&n,&m)!=EOF){
build(1,1,n);
while(m--){
scanf("%d %d %d",&a,&b,&c);
if(a==1) update(1,b,c);
else if(a==2) printf("%lld\n",query(1,b,c));
else FIB(1,b,c);
}
}
}