P1438 无聊的数列

题目来源:洛谷

题目背景

无聊的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)。

输出格式:

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

输入输出样例

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

说明

数据规模:

0≤n,m≤100000

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

Hint:

有没有巧妙的做法?

解析:

这道题我跟一条题解想到一块去了。

具体做法大概就是维护一条差分序列。为啥要维护差分序列呢?这个是可以从等差数列这个点看出来的。

等差数列的性质决定了它可以用差分序列维护,这就保证了线段树的可结合性。

跟不要说,题目写的很清楚的一则这个玩意:

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。

更是侧面应证了我们的思路的正确性。

打几个草稿就能发现此差分数列维护规律:

若差分序列为 d[],执行操作 1 l r k d

  1. d[l]=d[l]+k;

  2. d[i]=d[i]+d,l<i<=r;

  3. d[r+1]=d[r+1]-(k+d*(r-l));

至于具体原因,详见差分序列性质,思考一下很容易明白。

参考代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 100010
#define ll long long
using namespace std;
ll a[N],b[N];
struct segmentree{
    int l,r;
    ll dat,add;
}t[N<<2];
void build(int p,int l,int r)
{
    t[p].l=l;t[p].r=r;
    if(l==r){
        t[p].dat=b[l];
        return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    t[p].dat=t[p<<1].dat+t[p<<1|1].dat;
}
void pushdown(int p)
{
    if(t[p].add)
    {
        t[p<<1].add+=t[p].add;
        t[p<<1|1].add+=t[p].add;
        t[p<<1].dat+=t[p].add*(t[p<<1].r-t[p<<1].l+1);
        t[p<<1|1].dat+=t[p].add*(t[p<<1|1].r-t[p<<1|1].l+1);
        t[p].add=0;
    }
}
void change(int p,int l,int r,ll val)
{
    if(l<=t[p].l&&t[p].r<=r){
        t[p].add+=val;
        t[p].dat+=val*(t[p].r-t[p].l+1);
        return;
    }
    pushdown(p);
    int mid=(t[p].l+t[p].r)>>1;
    if(l<=mid) change(p<<1,l,r,val);
    if(r>mid) change(p<<1|1,l,r,val);
    t[p].dat=t[p<<1].dat+t[p<<1|1].dat;
}
ll query(int p,int l,int r)
{
    if(l<=t[p].l&&t[p].r<=r) return t[p].dat;
    pushdown(p);
    int mid=(t[p].l+t[p].r)>>1;
    ll val=0;
    if(l<=mid) val+=query(p<<1,l,r);
    if(r>mid) val+=query(p<<1|1,l,r);
    return val;
}
int main()
{
    int n,m,flag,l,r;
    ll k,d;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        b[i]=a[i]-a[i-1];//差分 
    }
    build(1,1,n);
    while(m--)
    {
        scanf("%d",&flag);
        if(flag==1){
            scanf("%d%d%lld%lld",&l,&r,&k,&d);
            //这个地方注意要特判
            //当需要修改的差分序列较特殊时,需要特殊处理 
            if(r==n&&l==r){
                change(1,l,l,k);
            }
            else if(r>=n){
                change(1,l,l,k);
                change(1,l+1,r,d);
            }
            else{
                change(1,l,l,k);
                change(1,l+1,r,d);
                change(1,r+1,r+1,-(k+d*(r-l)));//注意不用+1,由于差分序列改变的是l+1~r的值 
            }
        }
        else{
            scanf("%d",&r);
            printf("%lld
",query(1,1,r));
        }
    }
    return 0;
}

2019-05-17 14:23:49

原文地址:https://www.cnblogs.com/DarkValkyrie/p/10881105.html