值域线段树 (玲珑OJ 1117)

点击打开题目链接

题目意思很简单:

1、插入x
2、把小于x的数变成x
3、把大于x的数变成x
4、求集合中第x小数
5、求集合中小于x的数个数


思路: 线段树,节点是值的分数,你可以离散,也可以不离散,直接标记;我的写法是:   看代码注释>>>


据说数组改为指针会快点;代码比较挫.存一个;

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <iostream>
using namespace std;

typedef long long int LL;
const LL INF=1000000000;
const int maxn=6e6+100;


struct ACM
{
    struct segment
    {
        int val,lson,rson;
        bool mark;
    } seg[maxn];
    int sz;
    /***
        开辟新的节点并初始化
    */
    int newnode()
    {
        sz++;
        if(sz>=maxn)
        {
            int lzq=5,zhangpan=0;
            lzq=lzq/zhangpan;

        }
        seg[sz].lson=seg[sz].rson=-1;
        seg[sz].val=0,seg[sz].mark=false;
        return sz;
    }
    /***
        初始化根节点
    */
    void init(int& root)
    {
        sz=-1;
        root=newnode();
    }
    /***
        标记下放操作,如果没有节点开辟新的节点;
    */
    void pushdown(int i) 
    {
        int l=seg[i].lson,r=seg[i].rson;
        if(l==-1) seg[i].lson=newnode();
        if(r==-1) seg[i].rson=newnode();
        if(seg[i].mark)
        {
            seg[i].mark=false;
            seg[l].mark=seg[r].mark=true;
            seg[l].val=seg[r].val=0;
        }
    }
    void pushup(int i)
    {
        int l=seg[i].lson,r=seg[i].rson;
        seg[i].val=seg[l].val+seg[r].val;
    }
    /***
        插入函数 ; 在 pos 位置上面加上 val;
    */
    void ins(int i,int l,int r,int pos,int val)
    {
        if(l==r)
        {
            seg[i].val+=val;
            return ;
        }
        pushdown(i);
        int mid=(l+r)>>1;
        if(pos<=mid) ins(seg[i].lson,l,mid,pos,val);
        else ins(seg[i].rson,mid+1,r,pos,val);
        pushup(i);
    }
    /***
        将 L,R 这段区间上的值,更新为 0;
    */
    void del(int i,int l,int r,int L,int R)
    {
        if(l==L&&r==R)
        {
            seg[i].val=0;
            seg[i].mark=true;
            return ;
        }
        pushdown(i);
        int mid=(l+r)>>1;
        if(R<=mid) del(seg[i].lson,l,mid,L,R);
        else if(L>mid) del(seg[i].rson,mid+1,r,L,R);
        else del(seg[i].lson,l,mid,L,mid),del(seg[i].rson,mid+1,r,mid+1,R);
        pushup(i);
    }
    int find(int i,int l,int r,int x) //第x小的数字;
    {
        if(l==r) return l;
        pushdown(i);
        int lval=0;
        int mid=(l+r)>>1;
        if(seg[i].lson!=-1) lval=seg[seg[i].lson].val;
        if(x<=lval) return find(seg[i].lson,l,mid,x);
        else return find(seg[i].rson,mid+1,r,x-lval);
    }
    int query(int i,int l,int r,int L,int R) //区间 数的个数;
    {
        if(l==L&&r==R) return seg[i].val;
        pushdown(i);
        int mid=(l+r)>>1;
        if(R<=mid) return query(seg[i].lson,l,mid,L,R);
        else if(L>mid) return query(seg[i].rson,mid+1,r,L,R);
        else return query(seg[i].lson,l,mid,L,mid)+query(seg[i].rson,mid+1,r,mid+1,R);
    }
} AC;



int main()
{
    int m;
    scanf("%d",&m);
    int op,x,root,temp;
    AC.init(root);
    while(m--)
    {
        scanf("%d%d",&op,&x);
        switch(op)
        {
        case 1:
            AC.ins(root,0,INF,x,1);
            break;
        case 2:
            temp=AC.query(root,0,INF,0,x);
            AC.del(root,0,INF,0,x);
            AC.ins(root,0,INF,x,temp);
            break;
        case 3:
            temp=AC.query(root,0,INF,x,INF);
            AC.del(root,0,INF,x,INF);
            AC.ins(root,0,INF,x,temp);
            break;
        case 4:
            printf("%d\n",AC.find(root,0,INF,x));
            break;
        case 5:
            printf("%d\n",AC.query(root,0,INF,0,x-1));
            break;
        }
    }
    return 0;
}

代码二:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <iostream>
using namespace std;

const int INF=1000000000;
const int maxn=6e6+100;
struct ACM
{
    /***
        对于 2,3 操作,先查询个数,再插入这个数字:
        意思就是更新到底部,加上查询出来的个数;
        并加上清零的标记;
        
    */
    struct Node
    {
        int val,lson,rson;
        bool mark;
    } seg[maxn];
    int sz;
    int newnode()
    {
        sz++;
        seg[sz].val=0;
        seg[sz].lson=-1;
        seg[sz].rson=-1;
        seg[sz].mark=false;
        return sz;
    }
    void init(int& root)
    {
        sz=-1;
        root=newnode();
    }
    void pushdown(int i)
    {
        int l=seg[i].lson,r=seg[i].rson;
        if(l==-1) seg[i].lson=newnode();
        if(r==-1) seg[i].rson=newnode();
        if(seg[i].mark)
        {
            seg[i].mark=false;
            seg[l].mark=seg[r].mark=true;
            seg[l].val=seg[r].val=0;
        }
    }
    void pushup(int i)
    {
        int l=seg[i].lson,r=seg[i].rson;
        seg[i].val=seg[l].val+seg[r].val;
    }
    int query(int i,int l,int r,int L,int R) //区间 数的个数;
    {
        if(l==L&&r==R) return seg[i].val;
        pushdown(i);
        int mid=(l+r)>>1;
        if(R<=mid) return query(seg[i].lson,l,mid,L,R);
        else if(L>mid) return query(seg[i].rson,mid+1,r,L,R);
        else return query(seg[i].lson,l,mid,L,mid)+query(seg[i].rson,mid+1,r,mid+1,R);
    }
    void ins(int i,int l,int r,int pos,int val)
    {
        if(l==r)
        {
            seg[i].val+=val;
            return ;
        }
        pushdown(i);
        int mid=(l+r)>>1;
        if(pos<=mid) ins(seg[i].lson,l,mid,pos,val);
        else ins(seg[i].rson,mid+1,r,pos,val);
        pushup(i);
    }
    void operator2(int i,int l,int r,int x,int val)
    {
        if(l==r)
        {
            seg[i].val=val;
            return ;
        }
        int mid=(l+r)>>1;
        pushdown(i);
        if(x<=mid) operator2(seg[i].lson ,l,mid,x,val);
        else
        {
            seg[seg[i].lson].mark=true;
            seg[seg[i].lson].val=0;//如果,存在加到某一边,不存在也需要开新的节点!!
            operator2(seg[i].rson,mid+1,r,x,val);
        }
        pushup(i);
    }
    void operator3(int i,int l,int r,int x,int val)
    {
        if(l==r)
        {
            seg[i].val=val;
            return ;
        }
        pushdown(i);
        int mid=(l+r)>>1;
        if(x<=mid)
        {
            seg[seg[i].rson].mark=true;
            seg[seg[i].rson].val=0;
            operator3(seg[i].lson,l,mid,x,val);
        }
        else operator3(seg[i].rson,mid+1,r,x,val);
        pushup(i);
    }
    int operator4(int i,int l,int r,int x)
    {
        if(l==r) return l;
        int mid=(l+r)>>1;
        pushdown(i);
        int val=seg[seg[i].lson].val;
        if(x<=val) return operator4(seg[i].lson,l,mid,x);
        else return operator4(seg[i].rson,mid+1,r,x-val);
    }
} AC;


int main()
{

    int m;
    while(scanf("%d",&m)!=EOF)
    {
        int op,x,root,temp;
        AC.init(root);
        while(m--)
        {
            scanf("%d%d",&op,&x);
            switch(op)
            {
            case 1:
                AC.ins(root,0,INF,x,1);
                break;
            case 2:
                temp=AC.query(root,0,INF,0,x);
                AC.operator2(root,0,INF,x,temp);
                break;
            case 3:
                temp=AC.query(root,0,INF,x,INF);
                AC.operator3(root,0,INF,x,temp);
                break;
            case 4:
                printf("%d\n",AC.operator4(root,0,INF,x));
                break;
            case 5:
                printf("%d\n",AC.query(root,0,INF,0,x-1));
                break;
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有 $n$ 个点在轴上,每个点有一个权值 $a_i$,你需要支持以下操作: - 修改一个点的权值。 - 给出 $l,r,k$,询问在区间 $[l,r]$ ,权值严格大于 $k$ 的点的个。 输入格式 第一行一个正整 $n(1\leq n\leq 5\times10^5)$。 第二行 $n$ 个整 $a_i(|a_i|\leq 10^9)$,表示每个点的权值。 第三行一个正整 $m(1\leq m\leq 5\times10^5)$。 接下来 $m$ 行,每行一个操作,格式如下: - “Q l r k” 表示询问区间 $[l,r]$ ,权值严格大于 $k$ 的点的个。 - “C x y” 表示将第 $x$ 个点的权值修改为 $y$。 输出格式 对于每个询问操作,输出其结果。 输入样例 5 0 1 2 3 4 4 Q 2 5 3 C 4 6 Q 1 5 2 Q 3 4 4 输出样例 1 2 0 算法1 线段树(动态开点) 线段树的思想是把区间分成若干个小区间,每个小区间对应一段线段。对于每个线段,维护一些信息,例如区间和、区间最大值等等。 对于这道题目,我们可以按照值域线段树的思想,将区间对应到值域上。即将整个区间 $[0,n-1]$ 对应到值域上,建立一棵值域线段树。对于线段树上的每个节点,维护该节点对应的区间内权值大于某个值 $k$ 的点的个。当然,对于叶子节点,该值就是 $0$ 或 $1$。 对于一个查询操作 $Q(l,r,k)$,需要在值域线段树上找到 $[l,r]$ 对应的区间,然后查询该区间内权值大于 $k$ 的点的个。这个可以通过线段树区间查询操作实现。 对于一个修改操作 $C(x,y)$,需要在值域线段树上找到 $x$ 对应的叶子节点,然后修改该叶子节点的值为 $y$,然后向上更新整个线段树,直到根节点。 时间复杂度 对于每次修改和查询操作,都需要在值域线段树上查询或修改,时间复杂度是 $O(\log n)$。总时间复杂度是 $O(m\log n)$。 C++ 代码

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值