习题:V(线段树)

会有一股神秘感。 提交于 2020-01-23 11:09:03

题目

传送门

思路

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

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

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

所以我们转化下思路

线段树上维护操作

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

我们设标记 \((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)=\begin{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('\n');
            }
            if(opt==5)
            {
                pll t=ask_maxx(x,1);
                write(max(a[x]+t.x,t.y));
                putchar('\n');
            }
        }
    }
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!