Luogu P1198 BZOJ 1012 最大数 (线段树)

Luogu 同时被 3 个专栏收录
9 篇文章 0 订阅
12 篇文章 0 订阅
5 篇文章 0 订阅

URL: (Luogu) https://www.luogu.org/problem/show?pid=1198, (BZOJ)http://www.lydsy.com/JudgeOnline/problem.php?id=1012


题目大意:

给定一个数列,开始为空。维护两种操作:

(1) Q L表示查询当前数列后L个数的最大值。

(2) A N表示在当前数列末尾添加一个新数,这个新数的值等于上一次Q操作的答案加上给定的参数N. 若之前未进行Q操作,则添加的数为N.


思路分析:

此题牵扯到区间最值,我们可以用线段树进行解决。

但是Q操作由于需要插入元素,所以我们需要将其转化为能够利用线段树解决的问题。

这个序列是“动态”的,我们不妨将其变成”静态“的。

模拟添数的过程,对于每次查询,存下每次查询的区间左右端点,对于每次插入,记下插入的数以及它所对应的前一次查询的编号,便于后面找到上一次查询的答案。

然后,对记下的插入的数字先建线段树。

最后,循环枚举每次询问,在询问中继续枚举它所对应的插入,更新每次插入的值,以线段树单点修改的功能来实现。

看似先枚举询问,再枚举插入,是两重循环,但是由于在此循环中枚举插入的j只增不减(详见代码),因此不会超时。


部分易错点:

注意第0次询问(即前面没有询问)的情况要单列。


代码呈现:

(Luogu: Time: 712 MS; Memory: 12.05 MB; Code: 2.04 KB)

(BZOJ: Time: 1088 MS; Memory: 13324 KB; Code: 2333 B)

注: 由于不同OJ评测方式不尽相同,因此时间空间等结果有所差别,属正常现象

#include<cstdio>
#include<algorithm>
using namespace std;

const int MAXN = 2e5;
int MODN;
struct Node
{
    int left,right;
    int maxi;
};
struct SegmentTree
{
    Node nd[MAXN*4+2];
    
    void init()
    {
        for(int i=1; i<=MAXN*4; i++)
        {
            nd[i].left = nd[i].right = nd[i].maxi = 0;
        }
    }
    
    void build(int lbound,int rbound,int pos,int a[])
    {
        nd[pos].left = lbound;
        nd[pos].right = rbound;
        if(lbound==rbound)
        {
            nd[pos].maxi = a[lbound];
            return;
        }
        int mid = (lbound+rbound)/2;
        build(lbound,mid,2*pos,a);
        build(mid+1,rbound,2*pos+1,a);
        nd[pos].maxi = max(nd[2*pos].maxi,nd[2*pos+1].maxi);
    }
    
    void modify(int bound,int val,int pos)
    {
        int mid = (nd[pos].left+nd[pos].right)/2;
        if(nd[pos].left==nd[pos].right)
        {
            nd[pos].maxi = val;
            return;
        }
        if(bound<=mid) modify(bound,val,2*pos);
        else modify(bound,val,2*pos+1); 
        nd[pos].maxi = max(nd[2*pos].maxi,nd[2*pos+1].maxi);
    }
     
    int query(int lbound,int rbound,int pos)
    {
        int mid = (nd[pos].left+nd[pos].right)/2;
        if(lbound==nd[pos].left && rbound==nd[pos].right) return nd[pos].maxi;   
        int ans;
        if(rbound<=mid) ans = query(lbound,rbound,2*pos);
        else if(lbound>mid) ans = query(lbound,rbound,2*pos+1);
        else ans = max(query(lbound,mid,2*pos),query(mid+1,rbound,2*pos+1));
        return ans;
    }
};
SegmentTree st;
int a[MAXN+2];
int p[MAXN+2];
int ql[MAXN+2];
int qr[MAXN+2];
int n,m,q;

int main()
{
    char ch[5];
    int x;
    n = q = 0;
    scanf("%d%d",&m,&MODN);
    for(int i=1; i<=m; i++)
    {
        scanf("%s",ch);
        switch(ch[0])
        {
            case 'A':
            {
                scanf("%d",&x);
                a[++n] = x;
                p[n] = q;
                break;
            }
            case 'Q':
            {
                scanf("%d",&x);
                ql[++q] = n-x+1;
                qr[q] = n;
                break;
            }
        }
    }
    st.init();
    st.build(1,n,1,a);
    int j = 1;
    while(j<=n && !p[j])
    {
        j++;
    }
    for(int i=1; i<=q; i++)
    {
        int ans = st.query(ql[i],qr[i],1);
        printf("%d\n",ans);
        while(j<=n && p[j]==i)
        {
            st.modify(j,(int)((long long)a[j]+ans)%MODN,1);
            j++;
        }
    }
    
    return 0;
}

  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值