poj 3468 A Simple Problem with I…

Time Limit: 5000MSMemory Limit: 131072K

Description

You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa, Aa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

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

Hint

The sums may exceed the range of 32-bit integers.
 
题目大意:有长度为N的一列数,有M个操作,Q操作为询问l到r区间的数的和并输出,C操作是把l到r区间的每一个数都加上某个给出的值。
//================================================================================================
这题是可以用各种树做的。。。但是作为伸展树的练手也不错。
对于树上的每一个节点,都有七个域。分别记录父亲节点fa[i],两个子节点c[i,0]c[i,1],节点数值a[i],子树和sum[i],修改标签delta[i]和子树的节点个数size[i]。
每次操作都要向下推标签。询问的时候把l-1的节点伸展到根,把r+1的节点伸展到根的右节点,然后直接输出根的右节点的左子树的sum值。
因为询问时要伸展l-1和r+1两个节点,所以我们就有必要在原数列的头尾都再加上一个任意数,这样就能保证一定能找到l-1和r+1。
 
AC CODE
 
  
program pku_3468;
var a,sum,size,fa,delta:array[0..100010] of int64;
    c:array[0..100010,0..1] of longint;
    n,m,root:longint;
//============================================================================
procedure update(x:longint); //每次旋转完跟新节点的两个域。
begin
  sum[x]:=sum[c[x,0]]+sum[c[x,1]]+a[x];
  size[x]:=size[c[x,0]]+size[c[x,1]]+1; //要把本身加上。
end;
//============================================================================
procedure build(l,r:longint); //一开始先用递归建立伸展数,而不是一个个插入完在splay,这样省空间又省时间,不然有可能暴栈。
var mid,tmp:longint;
begin
  mid:=(l+r) shr 1;
  if l<mid then
  begin
    build(l,mid-1);
    tmp:=(l+mid-1) shr 1;
    c[mid,0]:=tmp; fa[tmp]:=mid;
  end;
  if mid<r then
  begin
    build(mid+1,r);
    tmp:=(mid+1+r) shr 1;
    c[mid,1]:=tmp; fa[tmp]:=mid;
  end; update(mid);
end;
//============================================================================
procedure init; //读入。
var i:longint;
begin
  readln(n,m);
  for i:=2 to n do read(a[i]); readln(a[n+1]);
  build(1,n+2); root:=(n+3) shr 1;
end;
//============================================================================
procedure clean(x,k:longint); //从x开始向下推标签,直到k。
begin
  repeat
    if delta[x]<>0 then
    begin
      inc(delta[c[x,0]],delta[x]);
      inc(delta[c[x,1]],delta[x]);
      inc(sum[c[x,0]],size[c[x,0]]*delta[x]);
      inc(sum[c[x,1]],size[c[x,1]]*delta[x]);
      a[x]:=a[x]+delta[x]; delta[x]:=0;
    end;
    if x=k then exit;
    if x>k then x:=c[x,0] else x:=c[x,1];
  until false;
end;
//============================================================================
procedure rotate(var root:longint; x:longint); //旋转x
var y,z,p,q:longint;
begin
  y:=fa[x]; z:=fa[y];
  if c[y,0]=x then p:=0 else p:=1;
  q:=p xor 1;
  if y=root then root:=x else
    if c[z,0]=y then c[z,0]:=x else c[z,1]:=x;
  fa[x]:=z; fa[y]:=x; fa[c[x,q]]:=y;
  c[y,p]:=c[x,q]; c[x,q]:=y;
  update(y); update(x); //旋转完要记住更新旋转中变动的节点的域。
end;
//============================================================================
procedure splay(var root:longint; x:longint); //重要的splay操作。
var y,z:longint;
begin
  while x<>root do
  begin
    y:=fa[x]; z:=fa[y];
    if y<>root then//判断是要双旋或是单旋。
      if (c[y,0]=x) xor (c[z,0]=y) then//用异或精炼判断是之字形还是一字型(zc所教Orz。。)
      rotate(root,x) else rotate(root,y);
    rotate(root,x);
  end;
end;
//============================================================================
procedure ask; //询问操作。
var s,t:longint;
begin
  readln(s,t); inc(t,2); //实际上是操作l-1和r+1两点。
  clean(root,s); clean(root,t); //先推标签。
  splay(root,s); splay(c[s,1],t);
  writeln(sum[c[t,0]]);
end;
//============================================================================
procedure change; //修改操作。
var s,t,z:longint;
begin
  readln(s,t,z); inc(t,2);
  clean(root,s); clean(root,t);
  splay(root,s); splay(c[s,1],t);
  inc(delta[c[t,0]],z); //伸展完可以直接把标签打在根的右节点的左节点上。
  inc(sum[c[t,0]],z*size[c[t,0]]); //打完标签要同时修改sum值。
end;
//============================================================================
procedure work; 
var i:longint;
    ch:char;
begin
  for i:=1 to m do
  begin
    read(ch);
    if ch='Q' then ask else change;
  end;
end;
//============================================================================
begin
  init;
  work;
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值