码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu P1438无聊的序列【线段树/差分】By cellur925

时间:2018-11-03 21:58:55      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:ble   前缀   clu   fine   cell   前缀和   思维   date   差分   

题目传送门

题目大意:维护一个序列,维护区间加等差数列,单点查询的操作。

首先我们肯定是要用线段树来维护了,按照一般的思维局限,我选择了维护序列中的值,但是区间修改的时候由于公差的存在,所以区间修改有些难搞。后来又想分别维护\(k\)\(d\),但是最终失败了。

正解十分巧妙,维护的是一个差分序列。如何维护?我们把\(l\)位置加上\(k\),把\([l,r)\)的位置加上\(d\),再把\(r+1\)的位置减去\(k+d*(r-l)\)。当查询的时候我们只要做一遍前缀和就好了。

然后??上一个线段树的区间修改+查询的板子就好了==。

#include<cstdio>
#include<algorithm>
#define maxn 100090

using namespace std;
typedef long long ll;

int n,m;
ll k,d,seq[maxn];
struct SegmentTree{
    int l,r;
    ll val,lazy;
}t[maxn*4];

void build(int p,int l,int r)
{
    t[p].l=l,t[p].r=r;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
}

void update(int p)
{
    if(!t[p].lazy||t[p].l==t[p].r) return ;
    t[p<<1].lazy+=t[p].lazy;
    t[p<<1|1].lazy+=t[p].lazy;
    t[p<<1].val+=t[p].lazy*(t[p<<1].r-t[p<<1].l+1);
    t[p<<1|1].val+=t[p].lazy*(t[p<<1|1].r-t[p<<1|1].l+1);
    t[p].lazy=0;
}

void change(int p,int l,int r,ll x)
{
    update(p);
    if(t[p].l==l&&t[p].r==r)
    {
        t[p].val+=1ll*x*(r-l+1);
        t[p].lazy+=x;
        return ;
    }
    int mid=(t[p].l+t[p].r)>>1;
    if(l>mid) change(p<<1|1,l,r,x);
    else if(r<=mid) change(p<<1,l,r,x);
    else change(p<<1,l,mid,x),change(p<<1|1,mid+1,r,x);
    t[p].val=t[p<<1].val+t[p<<1|1].val;
}

ll ask(int p,int l,int r)
{
    update(p);
    if(t[p].l==l&&t[p].r==r) return t[p].val;
    int mid=(t[p].l+t[p].r)>>1;
    if(l>mid) return ask(p<<1|1,l,r);
    else if(r<=mid) return ask(p<<1,l,r);
    else return ask(p<<1,l,mid)+ask(p<<1|1,mid+1,r);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&seq[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int op=0,p=0;
        scanf("%d",&op);
        if(op==1)
        {
            int l=0,r=0;
            scanf("%d%d%lld%lld",&l,&r,&k,&d);
            change(1,l,l,k);
            if(r>l) change(1,l+1,r,d);
            if(r!=n) change(1,r+1,r+1,-k-d*(r-l));
        }
        else if(op==2) scanf("%d",&p),printf("%lld\n",seq[p]+ask(1,1,p));
    }
    return 0;
}

注意:防止RE,修改的时候在\([l+1,r)\)区间改的时候要注意是不是\(l=r\);以及在\(r+1\)修改的时候判断是不是右区间为\(n\)

还是思维不要被僵化啊。

Luogu P1438无聊的序列【线段树/差分】By cellur925

标签:ble   前缀   clu   fine   cell   前缀和   思维   date   差分   

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9902076.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!