习题:V(线段树)

Posted loney-s

tags:

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

题目

传送门

思路

涉及到区间操作并且是静态的区间,用线段树是再合适不过的

如果直接维护每个点的权值不麻烦

但是要维护历史最大值就十分麻烦

所以我们转化下思路

线段树上维护操作

接着我们思考如何将操作统一化,并且是可叠加的,这样才能方便用懒标记

我们设标记 ((a,b))表示将x变为 (max(x+a,b))

区间加:((a,-INF))

区间赋值:((-INT,a))

将每个值变为(max(x_i-a,0))即为:((-a,0))

接着我们考虑合并的问题

假设现在的标记为((a,b)),从父亲节点传下来的标记为((c,d))

先将第一个标记表示出来(max(x+a,b)),这就是现在的(x')

再将(x') 代入

现在即为(max(c+max(x+a,b),d))

可以发现对于前一项的b,b与x无关

而我们定义标记的前一项是与x有关,而后一项与x无关

所以可以将其提取出来,即为

(max(c+a+x,max(c+b,d)))

所以合并之后的标记即为

((a+c,max(b+c),d))

现在已经解决了标记合并的问题

现在我们来考虑最大值的维护

如果我们将标记看成一个函数,

那么这个函数的图像一定是这样的:

技术图片

也就意味这合并之后的标记也是这样子的

同时注意到前面一段的平板是由标记的第二项决定的

后面一段上升的直线是由第一项决定的

(x_1<x_2),必然有(f(x_1)le f(x_2))

所以我们可以直接将现在的标记怼上去就行了

什么意思呢?

如果现在的最大值函数为(f(x))

当前的操作为(g(x))

那么新的最大值函数

(h(x)=egin{cases}f(x)[f(x)>g(x)]g(x)[其他情况]end{cases})

你看h很复杂,其实不然,如果你将两个函数转换成图像的形式之后

你就发现(h(x))的图像跟标记的函数图像一模一样

所以你就可以对标记的第一项取一个max,对后一项取一个max就行了

更新的时候最好写一个取值的边界的判断条件

笔者因此调了一个上午

别忘了开读入优化,UOJ对cin,cout不太友好,即使关了同步

代码

#include<iostream>
#include<cstdio>
using namespace std;
#define pll pair<long long,long long>
#define x first
#define y second
#define basic make_pair(0,0);
struct node
{
    int l;
    int r;
    pll val;
    pll maxx;
}tre[2000005];
int n,m;
int a[500005];
int opt,l,r,x;
void read(int &x)
{
    x=0;
    int f=1;
    char c=getchar();
    while('0'>c||c>'9')
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while('0'<=c&&c<='9')
    {
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    x*=f;
}
void write(long long x)
{
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
}
pll operator + (const pll &a,const pll &b)
{
    pll t;
    t.x=a.x+b.x;
    t.y=a.y+b.y;
    return t;
}
pll update_pair(pll a,pll b)
{
    pll t;
    t.x=a.x+b.x;
    t.y=max(a.y+b.x,b.y);
    return t;
}
void push_down(int k,int fa)
{
    pll t1=update_pair(tre[k].val,tre[fa].val);
    pll t2=update_pair(tre[k].val,tre[fa].maxx);
    tre[k].val.x=max(t1.x,-(1ll<<55));
    tre[k].val.y=t1.y;
    tre[k].maxx.x=max(tre[k].maxx.x,t2.x);
    tre[k].maxx.y=max(tre[k].maxx.y,t2.y);
}
void init(int k)
{
    tre[k].val.x=tre[k].maxx.x=0;
    tre[k].val.y=tre[k].maxx.y=-(1ll<<55);
}
void build(int l,int r,int k)
{
    tre[k].l=l;
    tre[k].r=r;
    tre[k].val=basic;
    tre[k].maxx=basic;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
}
void add(int l,int r,int k,pll a)
{
    if(tre[k].l>r||tre[k].r<l)
        return;
    if(l<=tre[k].l&&tre[k].r<=r)
    {
        pll t1=update_pair(tre[k].val,a);
        tre[k].val=t1;
        tre[k].maxx.x=max(tre[k].maxx.x,t1.x);
        tre[k].maxx.y=max(tre[k].maxx.y,t1.y);
        return;
    }
    push_down(k<<1,k);
    push_down(k<<1|1,k);
    init(k);
    add(l,r,k<<1,a);
    add(l,r,k<<1|1,a);
}
pll ask_now(int u,int k)
{
    if(tre[k].l>u||tre[k].r<u)
        return basic;
    if(tre[k].l==tre[k].r)
        return tre[k].val;
    push_down(k<<1,k);
    push_down(k<<1|1,k);
    init(k);
    return ask_now(u,k<<1)+ask_now(u,k<<1|1);
}
pll ask_maxx(int u,int k)
{
    if(tre[k].l>u||tre[k].r<u)
        return basic;
    if(tre[k].l==tre[k].r)
        return tre[k].maxx;
    push_down(k<<1,k);
    push_down(k<<1|1,k);
    init(k);
    return ask_maxx(u,k<<1)+ask_maxx(u,k<<1|1);
}
signed main()
{
    read(n);
    read(m);
    for(int i=1;i<=n;i++)
        read(a[i]);
    build(1,n,1);
    for(int i=1;i<=m;i++)
    {
        read(opt);
        if(opt<=3)
        {
            read(l);
            read(r);
            read(x);
            if(opt==1)
            {
                add(l,r,1,make_pair(x,-(1ll<<55)));
            }
            if(opt==2)
            {
                add(l,r,1,make_pair(-x,0));
            }
            if(opt==3)
            {
                add(l,r,1,make_pair(-(1ll<<55),x));
            }
        }
        else
        {
            read(x);
            if(opt==4)
            {
                pll t=ask_now(x,1);
                write(max(a[x]+t.x,t.y));
                putchar('
');
            }
            if(opt==5)
            {
                pll t=ask_maxx(x,1);
                write(max(a[x]+t.x,t.y));
                putchar('
');
            }
        }
    }
    return 0;
}

以上是关于习题:V(线段树)的主要内容,如果未能解决你的问题,请参考以下文章

PAT天梯赛练习题 L3-002. 堆栈(线段树查询第K大值)

线段树习题 总结

线段树合并

ACM入门之线段树习题

线段树练习题

求区间和(线段树)