题目地址
一般求和首先想到就是每个结点保存sum的值代表这个区间的和
那么求区间的和就是找到[s,e]这个区间的结点并返回该sum值,复杂度为O(logn)
但是这样子有个不好的地方就是假如最坏的情况下把1~n区间都加c值,那么每个1~n的叶子结点都要遍历到且+c ,则时间复杂度就为O(nlogn)
于是就在结点中保存Inc这个值,表示该区间[s,e]每个数都加了c,那么上述情况复杂度就变为O(1);
如下:
struct CNode{ //此结点sum=(L-R+1)*Inc+nSum; int L,R;LL nSum,Inc; //如果不设Inc,那么每次加数时都要更新到叶子节点 CNode *pLeft,*pRight;int Mid(){return (L+R)/2;}
}tree[maxn*2];
那么区间加值和求和的方式就要改变下
区间[s,e]加c值:遍历到该区间,把该结点Inc+=c; 途中遇到的区间就nSum+=c*(e-s); 把+c产生的结果加上
求[s,e]和:到该区间就返回 (e-s+1)*Inc+nSum ,途中遇到的区间就顺便把nSum的值算出来=Inc*区间长度,并把该点Inc的累加到其儿子的区间
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100000+5;
typedef long long LL;
struct CNode{ //此结点sum=(L-R+1)*Inc+nSum; int L,R;LL nSum,Inc; //如果不设Inc,那么每次加数时都要更新到叶子节点 CNode *pLeft,*pRight;int Mid(){return (L+R)/2;}
}tree[maxn*2];
int cnt=0;
void BuildTree(CNode *root,int L,int R)
{root->L=L;root->R=R;root->Inc=0;root->nSum=0;if(L==R) return;cnt++;root->pLeft=tree+cnt;cnt++;root->pRight=tree+cnt;int mid=root->Mid();BuildTree(root->pLeft,L,mid);BuildTree(root->pRight,mid+1,R);
}
void Insert(CNode *root,int i,int v)
{if(root->L==root->R){root->nSum=v;return;}root->nSum+=v; //更新根节点的值; if(i<=root->Mid()) Insert(root->pLeft,i,v);else Insert(root->pRight,i,v);
}
void Add(CNode *root,int L,int R,LL c)
{if(root->L==L&&root->R==R){root->Inc+=c;return;}int mid=root->Mid();root->nSum+=c*(R-L+1);if(R<=mid) Add(root->pLeft,L,R,c);else if(L>mid) Add(root->pRight,L,R,c);else {Add(root->pLeft,L,mid,c);Add(root->pRight,mid+1,R,c);}
}
LL Quary(CNode *root,int s,int e)
{if(root->L==s&&root->R==e){return root->nSum+root->Inc*(e-s+1);}root->nSum+=root->Inc*(root->R-root->L+1);int mid=root->Mid();Add(root->pLeft,root->L,mid,root->Inc);Add(root->pRight,mid+1,root->R,root->Inc);root->Inc=0; if(e<=mid) return Quary(root->pLeft,s,e);else if(s>mid) return Quary(root->pRight,s,e);else return Quary(root->pLeft,s,mid)+Quary(root->pRight,mid+1,e);
}
int main()
{int N,Q,v;scanf("%d%d",&N,&Q);BuildTree(tree,1,N);for(int i=1;i<=N;i++){scanf("%d",&v);Insert(tree,i,v);}char cmd[5];int s,e;for(int i=0;i<Q;i++){scanf("%s",cmd);if(cmd[0]=='Q') {scanf("%d%d",&s,&e);cout<<Quary(tree,s,e)<<endl;}else {scanf("%d%d%d",&s,&e,&v);Add(tree,s,e,v);}}return 0;
}