刷题记录(NC208250 牛牛的最美味和最不美味的零食)

这篇博客主要介绍了如何使用线段树解决区间删除和查询最大最小值的问题。文章给出了两种不同的实现方法,第一种是直接在区间内进行删除操作,第二种是先计算相对位置再进行操作。每种方法都包括了构建线段树、区间删除和区间查询的详细步骤,并提供了完整的C++代码示例。通过这两个方法,可以高效地处理动态更新和查询区间信息的复杂场景。
摘要由CSDN通过智能技术生成

NC208250 牛牛的最美味和最不美味的零食

题目链接

关键点:

因为题目是将节点将区间去掉,如何表示区间去掉,可以加一个数num表示当前区间里的元素个数。

法一:

区间里的删除节点操作和计算最大最小值的操作,根据当前区间的相对位置来计算

删除节点(删除第x个节点):

if (tree[2*p].num >= x) dlt(2*p, l, mid, x);//x在左区间
else dlt(2*p+1, mid+1, r, x-tree[p*2].num);

如果左区间的元素个数大于x,说明x在左区间内

如果元素个数小于x,说明x在右区间

查询区间[x, y](第x个元素到第y个元素)

if (tree[p*2].num>=y)//查询区间均在左孩子
        return find(2*p, l, mid, x, y);
    if (tree[p*2].num<x)//查询区间均在右孩子
        return find(2*p+1, mid+1, r, x-tree[2*p].num, y-tree[2*p].num);//[左区间的开端,左区间结束(相对位置)]
    ty t1 = find(p*2, l, mid, x, tree[2*p].num);
    ty t2 = find(p*2+1, mid+1, r, 1, y-tree[2*p].num);
    t1.maxx = max(t1.maxx, t2.maxx);//只要最低和最高
    t1.minn = min(t1.minn, t2.minn);
    return t1;

如果左区间的元素个数大于y,说明区间[x, y]全在左区间

如果左区间元素个数小于x,说明区间[x, y]全在右区间

否则,[x, y]就包括左右区间,找左区间[x, tree[2*p].num]+右区间[1, y-tree[2*p].num](从1开始)

完整代码:

# include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
struct ty
{
    int maxx, minn, num;
}tree[N*4];
int n, m;
int a[N];
void build(int p, int l, int r)
{
    if (l == r)
    {
        tree[p].maxx = a[l];
        tree[p].minn = a[l];
        tree[p].num = 1;
        return ;
    }
    int mid = (l+r)/2;
    build(2*p, l, mid);
    build(2*p+1, mid+1, r);
    tree[p].maxx = max(tree[p*2].maxx, tree[2*p+1].maxx);
    tree[p].minn = min(tree[p*2].minn, tree[2*p+1].minn);
    tree[p].num = tree[2*p].num + tree[2*p+1].num;
}
void dlt(int p, int l, int r, int x)
{
    if (l == r)//找到x位置
    {
        tree[p].maxx = -1e9-10;
        tree[p].minn = 1e9+10;
        tree[p].num = 0;
        return ;
    }
    int mid = (l+r)/2;
    if (tree[2*p].num >= x) dlt(2*p, l, mid, x);//x在左区间
    else dlt(2*p+1, mid+1, r, x-tree[p*2].num);
    tree[p].maxx = max(tree[p*2].maxx, tree[2*p+1].maxx);
    tree[p].minn = min(tree[p*2].minn, tree[2*p+1].minn);
    tree[p].num = tree[2*p].num + tree[2*p+1].num;
}
ty find(int p, int l, int r, int x, int y)//(x:当前区间查询区间开端,y:当前区间查询区间开端)
{
    if (x==1 && y==tree[p].num)
    {
        return tree[p];
    }
    int mid = (l+r)/2;
    if (tree[p*2].num>=y)//查询区间均在左孩子
        return find(2*p, l, mid, x, y);
    if (tree[p*2].num<x)//查询区间均在右孩子
        return find(2*p+1, mid+1, r, x-tree[2*p].num, y-tree[2*p].num);//[左区间的开端,左区间结束(相对位置)]
    ty t1 = find(p*2, l, mid, x, tree[2*p].num);
    ty t2 = find(p*2+1, mid+1, r, 1, y-tree[2*p].num);
    t1.maxx = max(t1.maxx, t2.maxx);//只要最低和最高
    t1.minn = min(t1.minn, t2.minn);
    return t1;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i=1; i<=n; i++)
        scanf("%d", &a[i]);
    build(1, 1, n);
    for (int i=1; i<=m; i++)
    {
        int k;
        scanf("%d", &k);
        if (k == 1)
        {
            int x;
            scanf("%d", &x);
            dlt(1, 1, n, x);
        }
        else
        {
            int x, y;
            scanf("%d%d", &x, &y);
            ty t;
            t = find(1, 1, n, x, y);
            printf("%d %d\n", t.minn, t.maxx);
        }
    }
    
    
    return 0;
}

法二:

先计算好相对位置,然后就可以正常的按照线段树模板来求

比方说x在线段树的相对位置进行转换

int calc(int p, int l, int r, int x)
{
    if (l == r)
        return l;
    int mid = (l+r)/2;
    if (tree[2*p].num>=x) return calc(2*p, l, mid, x);
    else return calc(2*p+1, mid+1, r, x-tree[2*p].num);
}

转换方法与上述同理

完整代码:

# include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
struct ty
{
    int maxx, minn, num;
}tree[N*4];
int n, m;
int a[N];
void build(int p, int l, int r)
{
    if (l == r)
    {
        tree[p].maxx = tree[p].minn = a[l];
        tree[p].num = 1;
        return ;
    }
    int mid = (l+r)/2;
    build(2*p, l, mid);
    build(2*p+1, mid+1, r);
    tree[p].maxx = max(tree[p*2].maxx, tree[2*p+1].maxx);
    tree[p].minn = min(tree[p*2].minn, tree[2*p+1].minn);
    tree[p].num = tree[2*p].num + tree[2*p+1].num;
}
void dlt(int p, int l, int r, int x)
{
    if (l == r)//找到x位置
    {
        tree[p].maxx = -1e9-10;
        tree[p].minn = 1e9+10;
        tree[p].num = 0;
        return ;
    }
    int mid = (l+r)/2;
    if (x<=mid) dlt(2*p, l, mid, x);//x在左区间
    else dlt(2*p+1, mid+1, r, x);
    tree[p].maxx = max(tree[p*2].maxx, tree[2*p+1].maxx);
    tree[p].minn = min(tree[p*2].minn, tree[2*p+1].minn);
    tree[p].num = tree[2*p].num + tree[2*p+1].num;
}
ty find(int p, int l, int r, int x, int y)//(x:当前区间查询区间开端,y:当前区间查询区间开端)
{
    if (x==l && r == y)
    {
        return tree[p];
    }
    int mid = (l+r)/2;
    if (y<=mid)//查询区间均在左孩子
        return find(2*p, l, mid, x, y);
    if (x>mid)//查询区间均在右孩子
        return find(2*p+1, mid+1, r, x, y);//[左区间的开端,左区间结束(相对位置)]
    ty t1 = find(p*2, l, mid, x, mid);
    ty t2 = find(p*2+1, mid+1, r, mid+1, y);
    t1.maxx = max(t1.maxx, t2.maxx);//只要最低和最高
    t1.minn = min(t1.minn, t2.minn);
    return t1;
}
int calc(int p, int l, int r, int x)
{
    if (l == r)
        return l;
    int mid = (l+r)/2;
    if (tree[2*p].num>=x) return calc(2*p, l, mid, x);
    else return calc(2*p+1, mid+1, r, x-tree[2*p].num);
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i=1; i<=n; i++)
        scanf("%d", &a[i]);
    build(1, 1, n);
    for (int i=1; i<=m; i++)
    {
        int k;
        scanf("%d", &k);
        if (k == 1)
        {
            int x;
            scanf("%d", &x);
            int a = calc(1, 1, n, x);
            dlt(1, 1, n, a);
        }
        else
        {
            int x, y;
            scanf("%d%d", &x, &y);
            int a = calc(1, 1, n, x), b = calc(1, 1, n, y);
            ty t;
            t = find(1, 1, n, a, b);
            printf("%d %d\n", t.minn, t.maxx);
        }
    }
    
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值