洛谷P1198 [JSOI2008] 最大数

题目链接:[JSOI2008] 最大数 - 洛谷

PS:本题解使用的是线段树解法。我们通读整个题目。

首先先说为什么要用线段树。我们可以看出来数组长度的最大值是2e5,并且每次查询的最坏的情况是O(N)的时间复杂度,这样一计算,发现最坏的情况是2e10,远大于1e8,必定TLE,所以使用查询复杂度为log(N)的线段树。假设每次查询的时候都是最坏的情况log(2e5)约等于17,这样时间复杂度降到了O(N)*10,完毕,不会TLE。

然后再说这题该怎么用线段树去写

首先我们得知道我们线段树维护的是什么,那么这题显而易见嘛,是区间的max值(后面我们的max值用mx代替,只有使用max函数的时候会使用max)

其次我们看题目中的操作。我们先看插入的操作,因为题目中说了初始时数列是空的,没有一个数。那么我们建树的时候可以让所有的节点变成0,包括线段树的叶子节点,这样每次插入的时候,相当于就是我们只要修改线段树的每一个叶子节点就行了,然后在pushup更新上面的节点就行了。

放出宏定义

#define int long long
#define lc u<<1//左孩子节点
#define rc u<<1|1//右孩子节点
const int N = 200010;
const int INF = -0x3f3f3f3f;//无穷小

放出Tree

struct Tree
{
    int l;
    int r;
    int mx;//维护的最大值
}tr[4*N];

先放出pushup的代码

我觉得pushup函数应该不需要过多的解释了吧,但是我这里还是解释一下好

父节点的mx值肯定是根据这个父节点的两个子节点来更新的,父节点的区间是两个孩子区间的合并,所以父节点的大区间的mx值肯定是取两个孩子区间的mx值嘛。

void pushup(int u)//u是父节点
{
    tr[u].mx = max(tr[lc].mx,tr[rc].mx);//向上更新mx值
}

这里放出建树的代码

void build(int u,int l,int r)//u是父节点
{
    tr[u]={l,r,0};//每一个节点都初始化为0
    if(l == r) return;//找到叶子节点
    int m = l+r>>1;//分裂
    build(lc,l,m);
    build(rc,m+1,r);
}

这里放出插入的代码(其实就是单点修改的代码)

void change(int u,int x,int v)
{
    if(tr[u].l == x && tr[u].r == x)//找到目标点位
    {
        tr[u].mx = v;
        return;
    }
    int m = tr[u].l+tr[u].r>>1;
    if(x<=m) change(lc,x,v);//如果小了,则找左子树
    if(x>m) change(rc,x,v);//大了就找右子树
    pushup(u);//最后修改完更新
}

然后就是查询的操作

这里放出查询的代码

int query(int u,int l,int r)
{
    if(l<=tr[u].l && tr[u].r<=r) return tr[u].mx;//如果查找的区间完全覆盖tr[u]的两边则直接return这个区间的mx值
    int m = tr[u].l+tr[u].r>>1;//否则就分裂
    int res = INF;//为了找出最大值
    if(l<=m) res = query(lc,l,r);
    if(r>m) res = max(res,query(rc,l,r));
    return res;
}

我们还要把查询的结果存起来,因为插入的时候需要用到上一次查询的结果嘛

这里我们放出solve()函数

void solve()
{
    cin>>m>>p;//p是模的数
    build(1,1,m);//建树,最多m个叶子节点

    char op;int x;
    int n = 0,t = 0;
    while(m--)
    {
        cin>>op>>x;
        if(op == 'A') change(1,++n,(x+t)%p);//++n是为了方便的插入第一个第二个一直到第n个数
        else 
        {
            t = query(1,n-x+1,n);//存起来查询的结果
            cout<<t<<endl;
        }
    }
}

还有不理解的可以把你的疑问放在评论区,不要忘记点赞+关注哟!!!

最后放出完整代码

#include <bits/stdc++.h>

using namespace std;

#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define int long long
#define lc u<<1
#define rc u<<1|1
const int N = 200010;
const int INF = -0x3f3f3f3f;
typedef long long LL;

int m,p;

struct Tree
{
    int l;
    int r;
    int mx;
}tr[4*N];

void pushup(int u)
{
    tr[u].mx = max(tr[lc].mx,tr[rc].mx);
}

void build(int u,int l,int r)
{
    tr[u]={l,r,0};
    if(l == r) return;
    int m = l+r>>1;
    build(lc,l,m);
    build(rc,m+1,r);
}

void change(int u,int x,int v)
{
    if(tr[u].l == x && tr[u].r == x)
    {
        tr[u].mx = v;
        return;
    }
    int m = tr[u].l+tr[u].r>>1;
    if(x<=m) change(lc,x,v);
    if(x>m) change(rc,x,v);
    pushup(u);
}

int query(int u,int l,int r)
{
    if(l<=tr[u].l && tr[u].r<=r) return tr[u].mx;
    int m = tr[u].l+tr[u].r>>1;
    int res = INF;
    if(l<=m) res = query(lc,l,r);
    if(r>m) res = max(res,query(rc,l,r));
    return res;
}

void solve()
{
    cin>>m>>p;
    build(1,1,m);

    char op;int x;
    int n = 0,t = 0;
    while(m--)
    {
        cin>>op>>x;
        if(op == 'A') change(1,++n,(x+t)%p);
        else 
        {
            t = query(1,n-x+1,n);
            cout<<t<<endl;
        }
    }
}

signed main()
{
    solve();
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法好玩头秃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值