• 数据结构~线段树,树状数组


    考虑一个无限完整的二叉搜索树(参见下图),节点中的数字是1,2,3,....在根节点为X的子树中,我们可以通过重复获得该子树中的最小数量沿着左边的节点往下走,直到最后一层,我们也可以通过沿着右边的节点找到最大数量。现在给出一些查询,“根节点为X的子树中的最小和最大数目是多少?” 请尝试找到有关查询的答案。 

    输入

    在输入中,第一行包含一个整数N,表示查询的数量。在接下来的N行中,每行都包含一个代表根号为X(1 <= X <= 2 31 - 1)的子树的数字 

    产量

    共有N行,其中第i行包含第i个查询的答案。

    示例输入

    2
    8
    10
    

    示例输出

    1 15
    9 11

    题意:求以当前为根节点下的最小值节点与最大值节点
    思路:乍一看不知道是啥,但是仔细看最小节点最大节点与根节点又有些关系,最小值节点=根节点-x 最大值节点=根节点+x
    但是每个对应的x值又不同,说明也与根节点的值有关系
    我们就拿几个例子进行比较
    我们只需要把用最小值把x求出来,最大值可以把x代进去求出
    6 5 化成二进制 110   101
    12 9   化成二进制 1100  1001
    8 1   化成二进制 1000  0001
    我们可以发现每个都是把二进制最低的1去掉再加1,然后我们求二进制最低的1有个特殊的办法 树状数组的lowbit 前不久刚学的23333
    所以x=lowbit-1
    那么最小值节点=x-lowbit(x)+1 最大值节点=x+lowbit(x)-1
    然后这题就简单了
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int lowbit(int x)
    {
        return x&(-x);
    }
    int main()
    {
        int n,x;
        scanf("%d",&n);
        while(n--)
        {
            scanf("%d",&x);
            printf("%d %d
    ",x-lowbit(x)+1,x+lowbit(x)-1);
        }
    }
    D. Xenia和位操作
    每次测试的时间限制
    2秒
    每次测试的内存限制
    256兆字节
    输入
    标准输入
    产量
    标准输出

    初学者程序员Xenia有一个序列a,由n个非负整数组成:1,  2,...,  nXenia目前正在研究位操作。为了更好地了解他们的工作,森雅决定计算一些数值v一个

    即,计算值v需要几次迭代在第一次迭代中,捷尼亚写入一个新的序列一个  一个2,  一个  一个4,...,  一个Ñ  - 1   一个Ñ,由2个ñ  - 1元件。换句话说,她写下序列a的相邻元素的按位或在第二次迭代中,Xenia写入了按位独占在第一次迭代之后获得的序列的相邻元素的OR。在第三次迭代中,Xenia写入在第二次迭代之后获得的序列的相邻元素的按位或。等等; 按位异或运算和按位异或运算。最后,她得到一个由一个元素组成的序列,该元素是v

    我们来看一个例子。假设序列a  =(1,2,3,4)然后让我们写下所有变换(1,2,3,4)  → (1    2 = 3,3    4 = 7)  →  (3  xor  7 = 4)结果是v  = 4

    给你Xenia的初始序列。但是要计算给定序列的v太容易了,所以给你额外的m个查询。每个查询都是一对整数p,  b查询p,  b意味着您需要执行赋值p  =  b每次查询后,您需要为新序列a打印新值v

    输入

    第一行包含两个整数Ñ (1≤  Ñ  ≤17,1≤    ≤10 5下一行包含2个Ñ整数一个1,  一个2,...,  一个Ñ(0≤  一个  <2 30每个下一个的线包含查询。个行包含整数,   (1个≤    ≤2 Ñ,0≤  b  <2 30- 第 i个查询。

    产量

    打印m个整数 - 第i个整数表示i个查询后的序列a的v

    例子
    输入
    复制
    2 4 
    1 6 3 5
    1 4
    3 4
    1 2
    1 2
    产量
    复制
    1 
    3
    3
    3
    题意:输入总共2^n个数,然后让相邻的两个数进行|操作或者^操作,当是第一次的时候是|,第二次是^,这样交替进行
    然后m次修改操作,这明显就是线段树的模板题,但是多了一个| ^ 的交替运算,可以判断这棵树的层数来判断是什么操作,然后发现n就是树的层数
    或者用全局变量递归到最底层的时候实时更新也行

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    struct sss
    {
        int left;
        int right;
        int value;
    }mp[ ((1 << 17) + 5)<<2];
    int n,m;
    int a[ (1 << 17) + 5];
    void build(int cnt,int left,int right,int op)
    {
        mp[cnt].left=left;
        mp[cnt].right=right;
        if(left==right)
        {
            mp[cnt].value=a[left];
            return ;
        }
        int mid=(left+right)/2;
        build(cnt<<1,left,mid,-op);
        build(cnt<<1|1,mid+1,right,-op);
        if(op==1)    mp[cnt].value=mp[cnt<<1].value|mp[cnt<<1|1].value;
        else    mp[cnt].value=mp[cnt<<1].value^mp[cnt<<1|1].value;
    }
    void updata(int cnt,int left,int right,int num,int value,int op)
    {
        if(left==right)
        {
            mp[cnt].value=value;
            return;
        }
        int mid=(left+right)/2;
         if(num<=mid) updata(cnt<<1,left,mid,num,value,-op);
         else updata(cnt<<1|1,mid+1,right,num,value,-op);
        if(op==1)    mp[cnt].value=mp[cnt<<1].value|mp[cnt<<1|1].value;
        else    mp[cnt].value=mp[cnt<<1].value^mp[cnt<<1|1].value;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int t=(int)pow(2,n);
        for(int i=1;i<=t;i++)
        {
            scanf("%d",&a[i]);
        }
        int op;
        if(n&1) op=1;
        else op=-1;
        build(1,1,t,op);
        int x,y;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            updata(1,1,t,x,y,op);
            printf("%d
    ",mp[1].value);
        }
    }
    B.队列
    每次测试的时间限制
    2秒
    每次测试的内存限制
    256兆字节
    输入
    标准输入
    产量
    标准输出

    在机场中n个海象站在队列中。它们从队列的尾部开始编号:第1个海象站在队列的尾部,第n个海象站在队列的开始处。第海象有年龄等于一个

    如果有一个年轻海象站在他的面前,即,如果存在这样的第海象变得不满Ĵ  <  Ĵ),即一个  >  一个Ĵi个海象不满他和前方最远的海象之间的海象数量相等,这比第i海象年轻也就是说,年轻的海象从他身上站得越远,这种不满就越强烈。

    机场经理要求你为排队中n个海象中的每一个都算数

    输入

    第一行包含一个整数Ñ2≤  Ñ  ≤10 5) -在队列海象的数量。第二行包含整数1≤  一个  ≤10 9)。

    请注意,有些海象可能具有相同的年龄,但是由于出现不满情绪,靠近队头的海象需要比其他海象年轻

    产量

    打印n个数字:如果第i个海象对所有事情都满意,打印“-1”(不含引号)。否则,就打印了个海象的不悦:那个站在他并从他最远的年轻海象之间的其他海象的数量。

    例子
    输入
    复制
    6 
    10 8 5 3 50 45
    产量
    复制
    2 1 0 -1 0 -1 
    输入
    复制
    7 
    10 4 6 3 2 8 15
    产量
    复制
    4 2 1 0 -1 -1 -1 
    输入
    复制
    5 
    10 3 1 10 11
    产量
    复制
    1 0 -1 -1 -1 

     题意:每个海象从后面选最远的一个比他小的数,算中间隔的个数

    用线段树维护一段区间的最小值,然后每个点查询完之后,把当前点的值置为MAX,这样下次搜寻的时候便不会搜到在他前面的数,算中间的数字个数就是算下标相减再加1、

    搜索的时候因为想尽量远,所以优先搜右子树是否有比当前数小的数,有则说明右边搜索中间数更加多

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define MAX 9999999999;
    using namespace std;
    struct sss
    {
        int left;
        int right;
        int value;
    }mp[100001<<4];
    int a[100001];
    void build(int cnt,int left,int right)
    {
        mp[cnt].left=left;
        mp[cnt].right=right;
        if(left==right)
        {
            mp[cnt].value=a[left];
            return;
        }
        int mid=(left+right)/2;
        build(cnt<<1,left,mid);
        build(cnt<<1|1,mid+1,right);
        mp[cnt].value=min(mp[cnt<<1].value,mp[cnt<<1|1].value);
    }
    void find(int cnt,int i,int num)
    {
        if(mp[cnt].left==mp[cnt].right)
        {
            printf("%d ",mp[cnt].left-i-1);
            return;
        }
        if(mp[cnt<<1|1].value<num)
        {
            find(cnt<<1|1,i,num);
        }
        else 
        {
            find(cnt<<1,i,num);
        }
    }
    void updata(int cnt,int num)
    {
        if(mp[cnt].left==mp[cnt].right)
        {
            mp[cnt].value=MAX;
            return;
        }
        int mid=(mp[cnt].left+mp[cnt].right)/2;
        if(num<=mid) updata(cnt<<1,num);
        else updata(cnt<<1|1,num);
        mp[cnt].value=min(mp[cnt<<1].value,mp[cnt<<1|1].value);
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        build(1,1,n);
        for(int i=1;i<=n;i++)
        {
            if(mp[1].value>=a[i]) printf("-1 ");
            else find(1,i,a[i]);
            updata(1,i);
        }
    }

    POJ 2828

    Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue…

    The Lunar New Year was approaching, but unluckily the Little Cat still had schedules going here and there. Now, he had to travel by train to Mianyang, Sichuan Province for the winter camp selection of the national team of Olympiad in Informatics.

    It was one o’clock a.m. and dark outside. Chill wind from the northwest did not scare off the people in the queue. The cold night gave the Little Cat a shiver. Why not find a problem to think about? That was none the less better than freezing to death!

    People kept jumping the queue. Since it was too dark around, such moves would not be discovered even by the people adjacent to the queue-jumpers. “If every person in the queue is assigned an integral value and all the information about those who have jumped the queue and where they stand after queue-jumping is given, can I find out the final order of people in the queue?” Thought the Little Cat.

    Input

    There will be several test cases in the input. Each test case consists of N + 1 lines where N (1 ≤ N ≤ 200,000) is given in the first line of the test case. The next N lines contain the pairs of values Posi and Vali in the increasing order of i(1 ≤ i ≤ N). For each i, the ranges and meanings of Posi and Vali are as follows:

    • Posi ∈ [0, i − 1] — The i-th person came to the queue and stood right behind the Posi-th person in the queue. The booking office was considered the 0th person and the person at the front of the queue was considered the first person in the queue.
    • Vali ∈ [0, 32767] — The i-th person was assigned the value Vali.

    There no blank lines between test cases. Proceed to the end of input.

    Output

    For each test cases, output a single line of space-separated integers which are the values of people in the order they stand in the queue.

    题意:开始数列中没有数,然后把慢慢插入进去,把当前要插入的数插入到指定位置,其余的数往后移

     思路:因为他前面插入的数的位置可能会被后面所影响,所以我们从后面往前面插入,利用线段树的单点操作,节点存该区间还有几个位置可以插入

    倒着插入的时候,先插入的数是确定的,不能改变,所以我们可以判断左区间的位置个数与索引相比较来确定他是左区间还是右区间,到右区间的时候

    要把索引值减去左区间的位置个数

    引用大牛博客:https://www.cnblogs.com/CheeseZH/archive/2012/04/29/2476134.html

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    struct sss
    {
        int left;
        int right;
        int value;
        int num;
    }mp[200001<<4];
    void build(int cnt,int left,int right)
    {
        mp[cnt].left=left;
        mp[cnt].right=right;
        if(left==right)
        {
            mp[cnt].value=0;
            mp[cnt].num=1;
            return;
        }
        int mid=(left+right)/2;
        build(cnt<<1,left,mid);
        build(cnt<<1|1,mid+1,right);
        mp[cnt].num=mp[cnt<<1].num+mp[cnt<<1|1].num;
        mp[cnt].value=0;
    }
    void updata(int cnt,int id,int value)
    {
        if(mp[cnt].left==mp[cnt].right)
        {
            mp[cnt].num--;
            mp[cnt].value=value;
            return;
        }
        if(mp[cnt<<1].num>=id) updata(cnt<<1,id,value);
        else updata(cnt<<1|1,id-mp[cnt<<1].num,value);
        mp[cnt].num=mp[cnt<<1].num+mp[cnt<<1|1].num;
    }
    int find(int cnt,int id)
    {
        if(mp[cnt].left==mp[cnt].right)
        {
            return mp[cnt].value;
        }
        int mid=(mp[cnt].left+mp[cnt].right)/2;
        if(mid>=id) return find(cnt<<1,id);
        else return find(cnt<<1|1,id);
    }
    int main()
    {
        int n;
        int a[200001],b[200001];
        int x,y;
        while(scanf("%d",&n)!=EOF)
        {
            build(1,1,n);
            for(int i=1;i<=n;i++)
            {
                scanf("%d%d",&a[i],&b[i]);
            }
            for(int i=n;i>=1;i--)
            {
                updata(1,a[i]+1,b[i]);
            }
            int flag=0;
            for(int i=1;i<=n;i++)
            {
                if(flag==0)
                {
                    flag=1;
                    printf("%d",find(1,i));
                }
                else printf(" %d",find(1,i));
            }
            printf("
    ");
        }
    }

    总结:线段树是一个灵活多变的一个玩意儿,但是实际上每次存的时候只有那么几种,所以重要的还是题目理清,灵活应变的去思考,以后还是要多做思维题

     
  • 相关阅读:
    两种三维点云密度聚类方法的研究与对比
    C++ string和char char*的转换
    七大排序算法总结
    map下标操作和insert区别
    c++ map 使用自定义结构做关键字
    BagFromImage安装与使用
    安装ipython notebook
    c++基本知识
    Python爬虫第一集
    91. Decode Ways
  • 原文地址:https://www.cnblogs.com/Lis-/p/9151973.html
Copyright © 2020-2023  润新知