st(树状数组版)

· · 个人记录

定理1、众所周知,线段树是拿来练习树状数组的(雾)。

定理2、用线段树能切ST表

结论:树状数组能切线段树 + 线段树切了ST表 = 树状数组能切ST表 √(逻辑鬼才)

所以我们来康康树状数组怎么维护区间最大值:

1、求lowbitlowbit
由于是树状数组,当然需要求lowbitlowbit

这点就不过多赘述了。如果不懂就去点最上面的链接awa。

int lowbit(int x)
{
    return x & -x;
}
2、建树,维护最大值
如何用树状数组维护最大值?

首先呢,我们知道树状数组如何维护区间和。本质是一个不断向上递归传递的过程。每次递归就把当前点的值给加上去。

维护区间和是加,维护最大值自然就是求最大值了。

void _add(int x, int k)//建树QAQ x表示树状数组点的编号,k表示需要传递的值
{
    for(;x <= n; x += lowbit(x))
        tree[x] = max(tree[x], k); //把 + 改为了max()
}

这个应该浅显易懂吧...

值得一提的是,这个函数还可以用来进行单点修改操作。 就像求区间和那样...但是这题不用啊QAQ。感觉有点大材小用了...

3、查询区间最大值(划重点)
上面有提到过,建树时可以直接套区间和的板子,只需要把累加改成求最值就可以了。

但是查询不行啊啊啊...这是为什么呢?

回想一下我们如何用树状数组求区间和? 假设我们要求出[x, y][x,y]的区间和,我们可以先求出[1,x-1][1,x−1]的区间和与[1,y][1,y]的区间和,再把他们相减。这样就求到了[x, y][x,y]的区间和啦。

但是最大值显然不行。当然你可以试试用[1,y][1,y]区间的最大值减[1,x-1][1,x−1]区间的最大值,看能不能输出正确答案。

好吧,那我们怎么处理查询问题??

我们分两种情况讨论:

1、 y-lowbit(y) > xy−lowbit(y)>x

这种情况下,我们可以把求[x,y][x,y]区间的最大值拆分成两部分,先求[x,y-lowbit(y)][x,y−lowbit(y)]中最大值与[y-lowbit(y)+1,y][y−lowbit(y)+1,y]中的最大值,再求这两者的最大值。

稍微细心一点,就可以发现 [y-lowbit(y)+1,y][y−lowbit(y)+1,y] 就是tree[y]tree[y] (可以自己口模一下哦)。

于是,问题就转化为:求 [x,y-lowbit(y)][x,y−lowbit(y)]中最大值 与 tree[y]tree[y]的最大值。

2、y-lowbit(y) < xy−lowbit(y)<x

在这种情况下,我们直接把 a[y]a[y](第yy个输入的数据)给剥离出来,于是原问就变成了:求 [x,y-1][x,y−1]中最大值 与 a[y]a[y] 的最大值。

好了,讨论完了。显而易见,我们把一个问题拆分成了两个子问题。这样做有什么好处?

好处就在于:我们可以用递归解决问题了!

由于我们拆分了原问题,所以不断递归的过程中,区间会越来越小。

递归到某一层时,x == yx==y,这个时候返回 a[x]a[x] 或 a[y]a[y] 就行了。(毕竟 x == yx==y时,区间[x,y][x,y]就只有a[x]a[x]这一个数据了)

所以我们就完成了这个过程(完全找不到区间和树状数组的影子了喂...)

int _findmax(int x, int y)//区间查询最大值 
{ 
    if(y > x)//区间长度不为1就拆分
    {
        if(y - lowbit(y) > x) return max(tree[y], _findmax(x, y - lowbit(y)));//情况1
        else return max(a[y], _findmax(x, y - 1));//情况2
    }
    return a[x];
}
4、合并代码
建树和查找都做完了,就可以合并代码了。

#include<bits/stdc++.h>
#define mian main
#define QWQ puts("QWQ");
using namespace std;

int n, m;
int a[100005] ,treex[100005];

int lowbit(int x)//求lowbit:2进制下末尾0的个数。可表示tree中包含数据数量 
{
    return x & -x;
}

void _add(int x, int k)//建树QAQ 
{
    for(;x <= n; x += lowbit(x))
        treex[x] = max(treex[x], k);
}

int _findmax(int x, int y)//区间查询最大值 
{ 
    if(y > x)
    {
        if(y - lowbit(y) > x) return max(treex[y], _findmax(x, y - lowbit(y)));
        else return max(a[y], _findmax(x, y - 1));
    }
    return a[x];
}

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++) 
    {
        scanf("%d", &a[i]);
        _add(i, a[i]);
    }
    for(int i = 1; i <= m; i ++)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        cout << _findmax(l, r) << endl;
    }
    return 0;
}
好了这篇题解到这里就结束了。

那是不可能的!

你交这篇题解上去,妥妥地被TT飞。

当然我们是不可能抛弃树状数组de。

于是我参照了某位毒瘤(lxl)的博客,就是这个

(真不愧是毒瘤!)

于是我们按照上面说的进行常数优化。

由于本题输入和输出都很多,所以快读和快写都是妥妥安排上的。

再者,代码里函数调用次数很多,因为都需要递归,直接加inlineinline。

最后,在循环里加registerregister 食用即可。

其他的我就没有再用了。因为已经可以卡过去了

直接从超时代码变成300ms+300ms+(数据最大的一个点)

最终代码
#include<bits/stdc++.h>
#define mian main
#define QWQ puts("QWQ");
using namespace std;

int n, m;
int a[100005] ,tree[100005];

inline int read()//读入优化 ,加 inline 进行优化 
{
    int s = 0, w = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

void print(int x)//输出优化(这题输出也蛮多的,所以优化一下) 
{
    if(x < 0) {putchar('-'); x =- x;}
    if(x > 9) print(x / 10);
    putchar(x % 10 + '0');
}

inline int lowbit(int x)//求lowbit:2进制下末尾0的个数。可表示tree中包含数据数量 
{
    return x & -x;
}

inline void _add(int x, int k)//建树QAQ 
{
    for(;x <= n; x += lowbit(x))
        tree[x] = max(tree[x], k);
}

inline int _findmax(int x, int y)//区间查询最大值 
{ 
    if(y > x)
    {
        if(y - lowbit(y) > x) return max(tree[y], _findmax(x, y - lowbit(y)));
        else return max(a[y], _findmax(x, y - 1));
    }
    return a[x];
}

int main()
{
    scanf("%d%d", &n, &m);
    for(register int i = 1; i <= n; i ++) //加 register 进行常数优化 
    {
        a[i] = read();
        _add(i, a[i]);
    }
    for(register int i = 1; i <= m; i ++)
    {
        int l, r;
        l = read(), r = read();
        print(_findmax(l, r));
        printf("\n");
    }
    return 0;
}