洛谷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();
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

算法好玩头秃

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

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

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

打赏作者

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

抵扣说明:

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

余额充值