转载请注明出处:http://blog.csdn.net/u012860063?viewmode=contents
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4893
----------------------------------------------------------------------------------------------------------------------------------------------------------
欢迎光临天资小屋:http://user.qzone.qq.com/593830943/main
----------------------------------------------------------------------------------------------------------------------------------------------------------
1 1 2 1 1 5 4 1 1 7 1 3 17 3 2 4 2 1 5
0 22
//hdu 4893:Wow! Such Sequence! //(线段树功能:单点更新,区间更新相邻较小斐波那契数) #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <set> using namespace std; #define ll __int64 //lson和rson分别表示结点的左儿子和右儿子 //rt表示当前子树的根(root),也就是当前所在的结点 #define lson l , mid , rt << 1 #define rson mid + 1 , r , rt << 1 | 1 //maxn是题目给的最大区间,而节点数要开4倍,确切的来说节点数要开大于maxn的最小2x的两倍 #define maxn 111111 ll num[maxn]; ll sum[maxn<<2];//求和 ll add[maxn<<2];//记录应该增加的值,有可能为负 int se[maxn<<2];//用来标记每个节点,为-1则表示没有标记,否则为标记; ll f[111]; void Pushup(int rt)//把当前结点的信息更新到父结点 { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; add[rt]=add[rt<<1]+add[rt<<1|1]; } void Pushdown(int rt,int l,int r)//把当前结点的信息更新给儿子结点 {//对某一个区间进行改变,如果被标记了,在查询的时候就得把改变传给子节点,因为查询的并不一定是当前区间 if(se[rt]!=-1) //已经标记过,该区间被改变过 { sum[rt<<1]+=add[rt<<1];//更新左儿子的和 sum[rt<<1|1]+=add[rt<<1|1];//更新右儿子的和 add[rt<<1]=add[rt<<1|1]=0;//还原应该增加的值为0 se[rt<<1]=se[rt<<1|1]=1; se[rt]=-1;//将标记向儿子节点移动后,父节点的延迟标记去掉 } } void build(int l,int r,int rt) { sum[rt]=0;//初始化为0; se[rt]=-1;//初始化为所有结点未被标记; if(l == r) { add[rt]=1; return ; } int mid = (l + r) >> 1; build(lson); build(rson); Pushup(rt); } void update(int L,int R,ll c,int l,int r,int rt) { if(L <= l && r <= R) { sum[rt]+=c; int x=(int)(lower_bound(f,f+77,sum[rt])-f);//寻找相应的斐波那契数 if(x == 0) { add[rt]=f[0]-sum[rt];//记录应该改变的差值 } else { if(f[x]==sum[rt]) { return ; } else if(sum[rt]-f[x-1]<=f[x]-sum[rt])//离的更近的 { add[rt]=f[x-1]-sum[rt]; } else add[rt]=f[x]-sum[rt]; } return ; } Pushdown(rt,l,r);//向下传递 int mid = (l + r) >> 1; if(L <= mid) update(L,R,c,lson);//更新左儿子 if(mid < R) update(L,R,c,rson);//更新右儿子 Pushup(rt);//向上传递更新和 } void ins(int L,int R,int l,int r,int rt) { if(L<=l && r<=R) { sum[rt]+=add[rt]; add[rt]=0;//还原为0 se[rt]=1; return ; } Pushdown(rt,l,r); int mid = (l + r) >> 1; if(L <= mid) ins(L,R,lson); if(mid < R) ins(L,R,rson); Pushup(rt); } ll query(int L,int R,int l,int r,int rt) { if(L<=l && r<=R) { return sum[rt]; }//要取rt子节点的值时,也要先把rt的延迟标记向下移动 Pushdown(rt,l,r); ll ret=0; int mid = (l + r) >> 1; if(L <= mid) ret+=query(L, R, lson); if(mid < R) ret+=query(L, R, rson); return ret; } int main() { int n, m; f[0]=1;f[1]=1; for(int i = 2; i <= 77; i++)//初始化为斐波那契数 f[i]=f[i-1]+f[i-2]; while(~scanf("%d%d",&n,&m))//n为节点数 { build(1,n,1);//建树 int op,a,b; for(int i = 1; i <= m; i++) { scanf("%d%d%d",&op,&a,&b); if(op==1) { update(a,a,b,1,n,1);//单点增加区间为a->a,b为增加值 } else if(op==2)//求和 { printf("%I64d\n",query(a,b,1,n,1)); } else if(op==3)//改变为斐波那契数 { ins(a,b,1,n,1); } } } return 0; }