A Simple Problem with IntegersTime Limit:5000MS Memory Limit:131072KB 64bit IO Format:%I64d & %I64u
Description
给出了一个序列,你需要处理如下两种询问。
"C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000)。
"Q a b" 询问[a, b]区间中所有值的和。
Input
第一行包含两个整数N, Q。1 ≤ N,Q ≤ 100000.
第二行包含n个整数,表示初始的序列A (-1000000000 ≤ Ai ≤ 1000000000)。
接下来Q行询问,格式如题目描述。
Output
对于每一个Q开头的询问,你需要输出相应的答案,每个答案一行。
Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
Sample Output
4
55
9
15
很明显的线段树的题目,因为在修改的时候是区间修改,这种修改相对于单点修改更加消耗时间,因此需要使用延时更新,说白了就是在更新的时候加入找到一个区间满足条件,那么只修改这个区间点的值,并且设置其标志位表示其子区间还没有被更新,等到再次更新或者查询的时候,需要用到它的子区间了,再更新,注意,这种延时是会累加的,即上次未更新设置的值和这次未更新设置的值会累加。
#include <iostream> #include <stdio.h> #define maxn 100000 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 typedef long long ll; using namespace std; ll add[maxn<<2];//延时更新标志位 ll sum[maxn<<2]; void push_up(ll rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void push_down(ll rt,ll num)//向下更新 { if(add[rt]) { add[rt<<1]+=add[rt];//设置子区间标志位 add[rt<<1|1]+=add[rt]; sum[rt<<1]+=add[rt]*(num-(num>>1));//更新子区间 sum[rt<<1|1]+=add[rt]*(num>>1); add[rt]=0;//重置标志位 } } void build(ll l,ll r,ll rt) { add[rt]=0; if(l==r) { scanf("%lld",&sum[rt]); return; } ll m=(l+r)>>1; build(lson); build(rson); push_up(rt); } void update(ll L,ll R,ll data,ll l,ll r,ll rt) { if(L<=l&&R>=r) { add[rt]+=data; sum[rt]+=data*(r-l+1); return; } push_down(rt,r-l+1);//需要用到子区间,向下更新 ll m=(l+r)>>1; if(L<=m) update(L,R,data,lson); if(R>m) update(L,R,data,rson); push_up(rt); } ll query(ll L,ll R,ll l,ll r,ll rt) { if(L<=l&&R>=r) { return sum[rt]; } push_down(rt,r-l+1);//需要用到子区间,向下更新 ll res=0; ll m=(l+r)>>1; if(L<=m) res+=query(L,R,lson); if(R>m) res+=query(L,R,rson); return res; } int main() { ll n,q; char op[2]; ll parm[3]; scanf("%lld%lld",&n,&q); build(1,n,1); while(q--) { scanf("%s%lld%lld",op,&parm[0],&parm[1]); switch(op[0]) { case 'Q': printf("%lld\n",query(parm[0],parm[1],1,n,1)); break; case 'C': scanf("%lld",&parm[2]); update(parm[0],parm[1],parm[2],1,n,1); break; } } return 0; }