### SEI说树状数组不能处理区间最值?

### SEI说树状数组不能处理区间最值?

众所周知,普通的树状数组处理区间和,都是运用容斥原理来完成运算的

但是,区间最值无法运用容斥原理来求,这怎么办呢?


### 不过,这就是很多人没有看到树状数组的本质了

我们来分析一下树状数组的节点:

f(1) for a(1)

f(2) for f(1)+a(2) = a(1)+a(2)

f(3) for a(3)

f(4) for f(2)+f(3)+f(4) = f(1~4)

f(5) ......

其中‘+’加号不一定是加法,也可用乘法,max,min等代替。

## 那么,我们可以发现什么?

对于f(x),它管辖的是(x-lowbit(x)+1)~x的所有元素,这也是树状数组最基本的定义。

也就是说,对于数x,我们求其的前缀和,就如同把其倍增地二进制分解为约莫log2(x)颗根节点范围为2^k的线段树,其中k为非负整数。

也就是倍增与分块。

那么,区间最值如何查询呢?

我们看到,由于前缀和是从1求起的,正好符合自然数的规律,所以分解为了多个2的非负整数次方的和。

那如果我们不让它从1求起呢?

当我们写树状数组时,经常会写到这种形式:

```cpp
......

int query(int x){
    int sum=0;
    while(x)
        sum+=t[x],x-=lowbit(x);
    return sum;
}

......
```


其中while循环与每次结束后的x-lowbit(x)等价于:

```cpp
for(;x>=1;x-=lowbit(x))
```

由于是前缀和,所以x>=1。

不难发现如果从下标2求起就是x>=2,从5求起就是x>=5,其中边界细节需要微调。

那么思路就很清晰了,如果求l~r区间的最值,循环即为(;r>=l;r-=lowbit(r)),其中一部分遍历到l之前,可以暂时不用树状数组,一格一格跳,等到遍历到管辖范围稍小的节点再用t[x]求值。

即:
```cpp
int query(int a,int b){
    int sum=-INF;
    while(b>=a){
        sum=max(sum,f[b]),--b;
        for(;b-lowbit(b)>=a;b-=lowbit(b))
            sum=max(sum,t[b]);
    }
    return sum;
}
```

很优秀,时间复杂度最劣也不到2log2(r-l+1)。

但是对于[P1886](https://www.luogu.com.cn/problem/solution/P1886)单调队列这题的魔鬼数据还是会跪下,怎么办呢?

引进一个小优化,另sum数组:

```cpp
sum[i]=max(sum[i-1],a[i])
```

容易发现当sum[r]>sum[r-l]时,sum[r]即为l~r区间,也是所有数组下标<=r的元素中的最大值

我也不知道为什么,这个小优化省下了大量的时间。

本题AC代码如下:

```cpp
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000001,INF=1145141919;
inline int read(){
    int x=0,f=1; char ch=getchar();
    while(ch<48||ch>57){if(ch=='-'){f=-1;}ch=getchar();}
    while(ch>=48&&ch<=57){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
    return x*f;
}
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
int n,m,k,l,r,f[maxn],t[maxn],f1[maxn],t1[maxn],s[maxn],s1[maxn];
int lowbit(int x){return x&-x;}
void update(int pos){
    while(pos<=n){
        t[pos]=f[pos],t1[pos]=f1[pos];
        for(int i=1;i<lowbit(pos);i<<=1)
            t[pos]=max(t[pos],t[pos-i]),t1[pos]=min(t1[pos],t1[pos-i]);
        pos+=lowbit(pos);
    }
}
int query(int a,int b){
    int sum=-INF;
    while(b>=a){
        sum=max(sum,f[b]),--b;
        for(;b-lowbit(b)>=a;b-=lowbit(b))
            sum=max(sum,t[b]);
    }
    return sum;
}
int query1(int a,int b){
    int sum=INF;
    while(b>=a){
        sum=min(sum,f1[b]),--b;
        for(;b-lowbit(b)>=a;b-=lowbit(b))
            sum=min(sum,t1[b]);
    }
    return sum;
}
int main()
{
    n=read(),k=read();
    for(int i=1;i<=n;++i)
        t1[i]=INF,t[i]=-INF;
    s[0]=INF,s1[0]=-INF;
    for(int i=1;i<=n;++i)
    {
        f[i]=f1[i]=read(),s[i]=min(s[i-1],f[i]),s1[i]=max(s1[i-1],f[i]);
        update(i);
    }
    for(int i=k;i<=n;++i)
        printf("%d ",s[i]!=s[i-k]?s[i]:query1(i-k+1,i));
    putchar(10);
    for(int i=k;i<=n;++i)
        printf("%d ",s1[i]!=s1[i-k]?s1[i]:query(i-k+1,i));
    return 0;
}
```


[P3865 ST表](https://www.luogu.com.cn/problem/P3865)同理。

第二篇题解,文章何处有误请见谅,望指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FFmpeg 是一个流行的开源多媒体处理工具,它提供了强大的功能,可以解析和处理各种音视频格式。SEI(Supplemental Enhancement Information)消息是一种附加的增强信息,用于传递一些额外的元数据或控制命令。 在 FFmpeg 中解析 SEI 消息可以通过解码音视频文件或流时获取。首先,我们需要使用 FFmpeg 的 avformat 模块打开音视频文件或流。然后,通过使用 demuxer 获取音视频流的 AVPacket,并将其传递给相应的解码器进行解码。 当解码器解码音视频帧时,如果存在 SEI 消息,解码器会将其提取并附加到输出帧的 AVFrame 结构中的 side_data 字段中。我们可以通过检查 AVFrame 结构的 side_data 字段,以及与 SEI 相关的元数据类型来判断是否存在 SEI 消息。 一旦确定存在 SEI 消息,我们可以进一步解析其中的信息。具体的解析方法取决于 SEI 消息的类型和格式。对于每种 SEI 消息类型,FFmpeg 提供了相应的结构体和函数,例如 AVActiveFormatDescription、AVDisplayMatrix、AVMasteringDisplayMetadata 等。 通过使用这些结构体和函数,我们可以获取 SEI 消息中的信息,例如视频帧的色彩空间、时间戳、显示方面比例等。可以根据具体的需求选择对应的结构体和函数进行解析。 总的来,通过 FFmpeg 解析 SEI 消息是一个相对简单的过程,只需要正确使用 FFmpeg 的解码器和相关函数,即可获得 SEI 消息中的相关信息。有了这些信息,我们可以进一步分析和处理音视频数据,实现更多的应用和功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值