题意:给定Q(1 ≤ Q≤ 100,000)个数A1,A2… AQ,,以及可能多次进行的两个操作:1)对某个区间Ai … Aj的每个数都加n(n可变)2) 求某个区间Ai … Aj间所有数的和
思路:线段树。注意每次更新不更新到叶节点,只更新到完整的区间为止。具体为:增加时如果改变的区间正好覆盖一个节点,则增加其节点的flag值,停止更新;否则更新sum值,再将增量往下传。在查询时,如果待查区间不是正好覆盖一个节点,就将节点的flag往下带,然后将flag代表的所有增量累加到sum上后将flag清0,接下来再往下查询。flag往下带的过程也是区间分解的过程,复杂度是O(log(n))。
#include <stdio.h> #include <string.h> #define N 100005 typedef struct node{ int left,right; __int64 sum; __int64 flag; struct node *leftson,*rightson; }tree; tree t[N*2+1]; tree *root,*alloc; int n,q; int getmid(tree *r){ return (r->left+r->right)/2; } void create(tree *r,int L,int R){ int mid; r->left = L; r->right = R; r->sum = r->flag = 0; if(L!=R){ mid = getmid(r); r->leftson = ++alloc; create(r->leftson,L,mid); r->rightson = ++alloc; create(r->rightson,mid+1,R); } } void insert(tree *r,int i,int x){ int mid = getmid(r); r->sum += x; if(r->left==i && r->right==i) return ; else if(i<=mid) insert(r->leftson,i,x); else insert(r->rightson,i,x); } void update(tree *r,int L,int R,__int64 c){ int mid = getmid(r); if(r->left==L && r->right==R){ r->flag += c; return ; } r->sum += (R-L+1)*c; if(R <= mid) update(r->leftson,L,R,c); else if(L > mid) update(r->rightson,L,R,c); else{ update(r->leftson,L,mid,c); update(r->rightson,mid+1,R,c); } } __int64 query(tree *r,int L,int R){ int mid = getmid(r); if(r->left==L && r->right==R) return r->sum + (R-L+1)*r->flag; r->sum += (r->right-r->left+1)*r->flag; update(r->leftson,r->left,mid,r->flag); update(r->rightson,mid+1,r->right,r->flag); r->flag = 0; if(R <= mid) return query(r->leftson,L,R); else if(L > mid) return query(r->rightson,L,R); else return query(r->leftson,L,mid)+query(r->rightson,mid+1,R); } int main(){ freopen("a.txt","r",stdin); while(scanf("%d %d",&n,&q)!=EOF){ int i,j,a,b,c; char ch; root = alloc = t; create(root,1,n); for(i = 1;i<=n;i++){ scanf("%d",&j); insert(root,i,j); } for(i = 1;i<=q;i++){ getchar(); ch = getchar(); if(ch == 'Q'){ scanf("%d %d",&a,&b); printf("%I64d\n",query(root,a,b)); }else{ scanf("%d %d %d",&a,&b,&c); update(root,a,b,c); } } } return 0; }
数组建线段树方法:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define N 100005 #define INF 0x3fffffff using namespace std; struct tree{ int l,r; long long sum,inc; }t[N<<2]; int n,q; int mid(int a,int b){ return (a+b)>>1; } void createtree(int root,int a,int b){ t[root].l = a; t[root].r = b; t[root].sum = t[root].inc = 0; if(a != b){ createtree(root*2, a, mid(a,b)); createtree(root*2+1, mid(a,b)+1, b); } } void insert(int root,int id,long long x){ int mm = mid(t[root].l,t[root].r); t[root].sum += x; if(t[root].l == t[root].r) return ; if(id <= mm) insert(root*2, id, x); else insert(root*2+1, id, x); } void add(int root,int a,int b,long long x){ int mm = mid(t[root].l,t[root].r); if(t[root].l == a && t[root].r == b){ t[root].inc += x; return; } t[root].sum += (b-a+1)*x; if(b <= mm) add(root*2,a,b,x); else if (a>mm) add(root*2+1, a, b, x); else{ add(root*2,a,mm,x); add(root*2+1,mm+1,b,x); } } long long query(int root,int a,int b){ int mm = mid(t[root].l,t[root].r); if(t[root].l == a && t[root].r == b) return t[root].sum + (b-a+1)*t[root].inc; t[root].sum += t[root].inc*(t[root].r-t[root].l+1); add(root*2,t[root].l ,mm, t[root].inc); add(root*2+1, mm+1, t[root].r , t[root].inc); t[root].inc = 0; if(b <= mm) return query(root*2, a, b); else if(a>mm) return query(root*2+1, a, b); else{ return query(root*2, a, mm) + query(root*2+1, mm+1, b); } } int main(){ int i,a,b; long long x; char ch; scanf("%d %d",&n,&q); createtree(1,1,n); for(i = 1;i<=n;i++){ scanf("%lld",&x); insert(1,i,x); } for(i = 1;i<=q;i++){ getchar(); ch = getchar(); if(ch == 'C'){ scanf("%d %d %lld",&a,&b,&x); add(1,a,b,x); }else{ scanf("%d %d",&a,&b); printf("%lld\n",query(1,a,b)); } } return 0; }