您的位置:首页 > 其它

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

2014-07-22 19:46 169 查看
题意:给定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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: