poj 3468

#include<stdio.h>
#define N 100001
struct node
{
int l,r;
__int64 sum;//记录各个节点包含区段的值得加和
__int64 type;//用来记录插入数字时的增量
int getmid()
{
return (l+r)>>1;
}
int getdis()
{
return r-l+1;
}
}p[3*N];

int a[N];

void build(int l,int r,int id)
{
p[id].l=l;
p[id].r=r;
p[id].type=0;
if(l==r)
{
p[id].sum=a[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,id<<1);
build(mid+1,r,id<<1|1);
p[id].sum=p[id<<1].sum+p[id<<1|1].sum;;//递归回来后 由左右孩子的sum值更新父亲的sum值
}

void update(int l,int r,__int64 type,int id)
{
if(p[id].l==l&&p[id].r==r)//找到区间后把区间所在节点的标记增量加上此次的增量
{
p[id].type+=type;
return;
}
p[id].sum+=type*(r-l+1);//给包含所找区间的节点+上要增加的量 
int mid=p[id].getmid();
if(r<=mid)
update(l,r,type,id<<1);
else if(l>mid)
update(l,r,type,id<<1|1);
else
{
update(l,mid,type,id<<1);
update(mid+1,r,type,id<<1|1);
}
}

__int64 query(int l,int r,int id)
{
int ll=id<<1;
int rr=id<<1|1;
if(p[id].l==l&&p[id].r==r)
return p[id].sum+p[id].type*p[id].getdis();
if(p[id].type!=0)
{
p[ll].type+=p[id].type;//更新左右孩子的标记增量
p[rr].type+=p[id].type;
p[id].sum+=p[id].type*p[id].getdis();//更新父节点的sum
p[id].type=0;//已经把增量传给左右孩子,清0,不然会有重复
}
int mid=p[id].getmid();
if(r<=mid)
return query(l,r,ll);
else if(l>mid)
return query(l,r,rr);
else
return query(l,mid,ll)+query(mid+1,r,rr);
}

int main()
{
int n,m;
scanf("%d%d",&n,&m);
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,n,1);
while(m--)
{
char ch[5];
int x,y;
__int64 c;
scanf("%s%d%d",ch,&x,&y);
if(ch[0]=='Q')
printf("%I64d\n",query(x,y,1));
else
{
scanf("%I64d",&c);
update(x,y,c,1);
}
}
}
return 0;
}
/*
摘自某大牛:
update中,p[id].sum+=type*(r-l+1)注意,若当前的l,r已经不是满整个区间的时候,
我们不能用区间的长度乘以标记来表示这个区间的和了,相对应的我们要用sum这个值来记录。
 
query中,如果此时的区间不是我们要找的,那么要把值传给他的左右孩子,
相似的,此时id的区间标记要设置为0,id区间的和要改用sum来表示。如果不设置为0会产生重复传递,
比如计算【1,1】的时候,【1,3】将值传给【1,2】,【1,2】传给【1,1】,从而计算出【1,1】区间的值,
如果【1,3】,【1,2】不标记为0,那么如果下一个询问的是【1,2】,此时区间【1,3】又会将值传递给【1,2】,
但是这个传递的其实在计算【1,1】时候已经传过了,从而导致重复,结果大于真实的。
既然一定要设置为0,那么此时【1,2】,【1,3】的区间和虽然可以直接计算,但是要把和转为sum来保存。*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值