无聊的数列线段树

Posted qseer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无聊的数列线段树相关的知识,希望对你有一定的参考价值。

题目背景

无聊的YYB总喜欢搞出一些正常人无法搞出的东西。有一天,无聊的YYB想出了一道无聊的题:无聊的数列。。。(K峰:这题不是傻X题吗)

题目描述

维护一个数列{a[i]},支持两种操作:

1、1 L R K D:给出一个长度等于R-L+1的等差数列,首项为K,公差为D,并将它对应加到a[L]~a[R]的每一个数上。即:令a[L]=a[L]+K,a[L+1]=a[L+1]+K+D,

a[L+2]=a[L+2]+K+2D……a[R]=a[R]+K+(R-L)D。

2、2 P:询问序列的第P个数的值a[P]。

输入输出格式

输入格式:

 

第一行两个整数数n,m,表示数列长度和操作个数。

第二行n个整数,第i个数表示a[i](i=1,2,3…,n)。

接下来的m行,表示m个操作,有两种形式:

1 L R K D

2 P 字母意义见描述(L≤R)。

 

输出格式:

 

对于每个询问,输出答案,每个答案占一行。

 

输入输出样例

输入样例
5 2
1 2 3 4 5
1 2 4 1 2
2 3
输出样例
6

说明

数据规模:

0≤n,m≤100000

|a[i]|,|K|,|D|≤200


神奇的线段树,本来线段树的用法都是静态地操作,现在发现连这样的动态的等差数列都可以添加

其实关键是利用了差分的原理,通过线段树可以很好地发挥差分的优势

code

#include<stdio.h> 
#include<algorithm> 
#define ls x<<1
#define rs x<<1|1
using namespace std;
const int mxn=100010;
int n,m,K,D;
int a[mxn],laz[mxn<<2],tree[mxn<<2];

inline int In() {
    int s=0,f=1;
    char ch=getchar();
    while(ch>9 || ch<0) {
        if(ch==-) f=-1;
        ch=getchar();
    }
    while(ch>=0 && ch<=9) s=s*10+ch-0,ch=getchar();
    return s*f;
}

void pd(int x,int l,int r,int mid) {
    if(laz[x]) {
        laz[ls]+=laz[x];
        laz[rs]+=laz[x];
        tree[ls]+=(mid-l+1)*laz[x];
        tree[rs]+=(r-mid)*laz[x];
        laz[x]=0;
    }
}

void add(int x,int l,int r,int ql,int qr,int val) 
{
    if(ql<=l && qr>=r) {
        tree[x]+=val*(r-l+1),laz[x]+=val;
        return ;
    }
    int mid=(l+r)>>1;
    pd(x,l,r,mid);
    if(ql<=mid) add(ls,l,mid,ql,qr,val);
    if(qr>mid) add(rs,mid+1,r,ql,qr,val);
    tree[x]=tree[ls]+tree[rs];
}

int qur(int x,int l,int r,int ql,int qr)  
{
    if(ql<=l && qr>=r) return tree[x];
    int mid=(l+r)>>1,re=0;
    pd(x,l,r,mid);
    if(ql<=mid) re+=qur(ls,l,mid,ql,qr);
    if(qr>mid) re+=qur(rs,mid+1,r,ql,qr);
    return re;
}

int main() 
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    for(int i=1;i<=m;++i) {
        int cas;
        scanf("%d",&cas);
        if(cas==1) {
            int ql,qr;
            scanf("%d%d%d%d",&ql,&qr,&K,&D);
            add(1,1,n,ql,ql,K);
            if(qr>ql) add(1,1,n,ql+1,qr,D);
            if(qr!=n) add(1,1,n,qr+1,qr+1,-K-(qr-ql)*D);
        }
        else if(cas==2) {
            int x;
            scanf("%d",&x);
            printf("%d
",a[x]+qur(1,1,n,1,x));
        }
    }
    return 0;
}

 



以上是关于无聊的数列线段树的主要内容,如果未能解决你的问题,请参考以下文章

[LuoguP1438]无聊的数列(差分+线段树/树状数组)

[Luogu] P1438 无聊的数列 | 线段树简单题

luogu P1438 无聊的数列 |差分+线段树

luogu1438无聊的数列(区间加等差数列,求一个数的和)

Luogu1438 无聊的数列

P1438 无聊的数列