【题解】LuoGu3372:【模板】线段树 1【两解】

题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和
输入输出格式
输入格式:
第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k
操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和
输出格式:
输出包含若干行整数,即为所有操作2的结果。
输入输出样例
输入样例#1:
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出样例#1:
11
8
20
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=8,M<=10
对于70%的数据:N<=1000,M<=10000
对于100%的数据:N<=100000,M<=100000
(数据已经过加强^_^,保证在int64/long long数据范围内)

【题解】

区间修改+区间查询

博主这里讲(主要还是博主自己的回顾)一下线段树和树状数组的做法
【线段树】
线段树还是很好理解的,只是博主也有一段时间没码过了,今天花了点时间码,博主总共错了三次

  1. 用的c++,然后编好后出现了玄学错误(至少我是这样认为的。。),c++的码力还是不行啊,所以我改用了pascal
  2. 既然是区间每个数加一个值,那么区间总和应该加区间长*delta,博主在update和传lazytag时都忘了区间长
  3. 忘开int64(long long)了,wa3个点

Code:

type
    Node=record
        num,tag:int64;
    end;
var
    tree:array[0..1000000] of Node;
    a:array[0..1000000] of int64;
    n,m,x,y,z,i:longint;

procedure build(root,l,r:longint);
var
    mid:longint;

begin
    if l=r then
    begin
        tree[root].num:=a[l];
        exit;
    end;
    mid:=(l+r)>>1;
    build(root<<1,l,mid); build(root<<1+1,mid+1,r);
    tree[root].num:=tree[root<<1].num+tree[root<<1+1].num;
end;

procedure pushdown(root,l,r:longint);
var
    mid:longint;

begin
    mid:=(l+r)>>1;
    inc(tree[root<<1].num,(mid-l+1)*tree[root].tag);
    inc(tree[root<<1+1].num,(r-mid)*tree[root].tag);
    inc(tree[root<<1].tag,tree[root].tag);
    inc(tree[root<<1+1].tag,tree[root].tag);
    tree[root].tag:=0;
end;

procedure update(root,tl,tr,l,r,delta:longint);
var
    mid:longint;

begin
    if (tl>r) or (tr<l) then exit;
    if (tl>=l) and (tr<=r) then
    begin
        inc(tree[root].num,(tr-tl+1)*delta);
        inc(tree[root].tag,delta);
        exit;
    end;
    if (tree[root].tag>0) then pushdown(root,tl,tr);
    mid:=(tl+tr)>>1;
    update(root<<1,tl,mid,l,r,delta);
    update(root<<1+1,mid+1,tr,l,r,delta);
    tree[root].num:=tree[root<<1].num+tree[root<<1+1].num;
end;

function query(root,tl,tr,l,r:longint):int64;
var
    mid:longint;

begin
    if (tl>r) or (tr<l) then exit(0);
    if (tl>=l) and (tr<=r) then exit(tree[root].num);
    if (tree[root].tag>0) then pushdown(root,tl,tr);
    mid:=(tl+tr)>>1;
    query:=0;
    inc(query,query(root<<1,tl,mid,l,r)+query(root<<1+1,mid+1,tr,l,r));
end;

begin
    readln(n,m);
    for i:=1 to n do read(a[i]);
    build(1,1,n);
    for i:=1 to m do
    begin
        read(x);
        if x=1 then
        begin
            readln(x,y,z);
            update(1,1,n,x,y,z);
        end else
        begin
            readln(x,y);
            writeln(query(1,1,n,x,y));
        end;
    end;
end.

【树状数组】
要用到差分
若a[i]为序列中各个元素
我们令delta[i]=a[i]-a[i-1],那么有a[i]=delta[1]+delta[2]+delta[3]+···+delta[i-1]+delta[i]

那么此题,我们将初始数据与更新数据分开来,先维护一个初始前缀和sum[i]=sum[1]+sum[2]+sum[3]+···+sum[i-1]+sum[i],delta记录更新的数据,即delta[1]+delta[2]+delta[3]+···+delta[i]表示第i个数更新了多少

所以,1~i的和为:
sum[i]+i*delta[1]+(i-1)*delta[2]+(i-2)*delta[3]+···+2*delta[i-1]+delta[i]
=sum[i]+sigma(delta[x]*(i-x+1))
=sum[i]+(i-1)*sigma(delta[x])+sigma(delta[x]*x)
再令delta1[i]=delta[i]*i,那么此题就搞定了
Code:

type
    ar=array[0..100000] of int64;
var
    sum,delta,delta1:ar;
    l,r,x,y,i,n,m:longint;
function lowbit(x:int64):int64;
begin
    exit(x and -x);
end;
procedure change(var delta:ar;x,y:int64);
begin
    while x<=n do
        begin
            inc(delta[x],y);
            inc(x,lowbit(x));
        end;
end;
function getsum(var delta:ar;x:int64):int64;
begin
    getsum:=0;
    while x>0 do
        begin
            inc(getsum,delta[x]);
            dec(x,lowbit(x));
        end;
end;
begin
    readln(n,m);
    for i:=1 to n do
        begin
            read(x);
            sum[i]:=sum[i-1]+x;
        end;
    for i:=1 to m do
        begin
            read(x);
            if x=1 then
                begin
                    readln(l,r,x);
                    change(delta,l,x);
                    change(delta,r+1,-x); //delta的差分
                    change(delta1,l,x*l);
                    change(delta1,r+1,-x*(r+1));  //delta1的差分
                end else
                begin
                    readln(l,r);
                    writeln(sum[r]-sum[l-1]+(r+1)*getsum(delta,r)-l*getsum(delta,l-1)-getsum(delta1,r)+getsum(delta1,l-1));
                end;
        end;
end.

Ps:树状数组的Code是一年前的老程序了(博主懒得再码了),码风略诡异,望谅解~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值