求和问题(线段树维护等差数列)

题目描述

你现在有一个数组 AA

我们定义如下的两种操作:

1. 修改: 形如 00 ll rr ,效果为对所有 l<=i<=rl<=i<=r 执行 Ai+=(i−l+1)Ai+=(i−l+1)

直观地说就是Al+=1,Al+1+=2,Al+2+=3 ... Ar+=r−l+1Al+=1,Al+1+=2,Al+2+=3 ... Ar+=r−l+1 这个样子

2. 查询: 形如 11 ll rr , 要求输出此时的 ∑ri=lAi∑i=lrAi 的值

一开始当然整个数组都是0啦

 

输入描述

第一行一个整数Q表示操作总数

接下来Q行, 每行一个操作(格式参考题目描述)

 

输出描述

对于每个查询, 输出一行, 表示此时的 ∑ri=lAi∑i=lrAi 的值

 

样例输入

3
0 1 2
0 3 4
1 1 4

 

样例输出

6

数据范围

1<=Q,l,r<=105

最近给学弟学妹讲线段树,发现以前这道题并没有补,就给补上了。
对于一个区间每个位置要加上对应的数   [1 2 3 4 5 6 ...]  

若把上述区间分成左右区间就变成了         [1 2 3]     [4 5 6] 

                                                 a           b
                      由于  [4 5 6] =  [1 2 3] + [3 3 3]
所以需要两个懒惰标记,lazy1记录这个区间里有多少个等差数列(a部分),lazy2记录这个区间的每个数要加上多少(b部分)

 

代码如下:

#include<bits/stdc++.h>
#define N 100010
#define ll long long
using namespace std;
struct node
{
    int op,l,r;
}eg[N];
ll Sum[N<<2],lazy1[N<<2],lazy2[N<<2];
void pushup(int o)
{
    Sum[o]=Sum[o<<1]+Sum[o<<1|1];
}
void pushdown(int o,ll len)
{
    if(lazy1[o])
    {
        lazy1[o<<1]+=lazy1[o];   //等差数列个数下传
        lazy1[o<<1|1]+=lazy1[o];

        ll l1=len-len/2;   //左儿子的长度
        ll l2=len/2;

        lazy2[o<<1]+=lazy2[o];
        lazy2[o<<1|1]+=lazy2[o]+l1*lazy1[o]; //不仅要下传lazy2,还要下传 lazy1个等差数列的前半部分

        Sum[o<<1]+=(lazy1[o]*(l1+1)*l1/2)+lazy2[o]*l1;
        Sum[o<<1|1]+=(lazy1[o]*(l2+1)*l2/2)+(lazy2[o]+l1*lazy1[o])*l2;

        lazy1[o]=lazy2[o]=0;
    }

}
void update(int x,int y,int l,int r,int o)
{

    if(l>=x&&r<=y)
    {
        ll len=(ll)(r-l+1);
        ll front_len=(ll)l-x;   // 因为l>=x的  所以是把[l,r]当作:[1,2,3,..len] + [front_len,front_len,front_len ...front_len]
        Sum[o]+=((len+1)*len)/2+len*front_len;
        lazy1[o]++;
        lazy2[o]+=front_len;
        return ;
    }
    pushdown(o,(ll)r-l+1);
    int mid=(l+r)>>1;
    if(x<=mid)update(x,y,l,mid,o<<1);
    if(y>mid)update(x,y,mid+1,r,o<<1|1);
    pushup(o);
}
ll query(int x,int y,int l,int r,int o)
{

    if(l>=x&&r<=y)return Sum[o];
    pushdown(o,(ll)r-l+1);
    int mid=(l+r)>>1;
    ll sum=0;
    if(x<=mid)sum+=query(x,y,l,mid,o<<1);
    if(y>mid)sum+=query(x,y,mid+1,r,o<<1|1);
    return sum;
}
int main()
{
    int q;
    scanf("%d",&q);
    int n=0;
    for(int i=0;i<q;i++)
    {
        scanf("%d%d%d",&eg[i].op,&eg[i].l,&eg[i].r);
        if(eg[i].l>eg[i].r)swap(eg[i].l,eg[i].r);
        n=max(n,eg[i].r);
    }
    for(int i=0;i<q;i++)
    {
        if(eg[i].op==0)update(eg[i].l,eg[i].r,1,n,1);
        else printf("%lld\n",query(eg[i].l,eg[i].r,1,n,1));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值