poj 3468 线段树(区间和,有更新)

题意:给定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;
}


你可能感兴趣的:(poj 3468 线段树(区间和,有更新))