zoukankan      html  css  js  c++  java
  • POJ2828线段树单点更新——逆序更新

    Description
    输入n个有序对< pi, vi >,pi表示在第pi个位置后面插入一个值为vi的人,并且pi是不降的。输出最终得到的v的序列
    Input
    多组用例,每组用例第一行为有序对组数n,之后n行每行两个整数表示一个有序对,以n=0结束输入
    Output
    对于每组用例,输出最后得到的序列
    Sample Input
    4
    0 77
    1 51
    1 33
    2 69
    4
    0 20523
    1 19243
    1 3890
    0 31492
    Sample Output
    77 33 69 51
    31492 20523 3890 19243

    单点插入线段树——所以建树的时候叶子节点应该为大于n的第一个数(cnt = 2 ^ k  > n)

    建树的时候我们logn找到该点向下更新叶子和向上更新根节点特别的我们要记录这个节点还有多少空位

    为什么要逆序?

    因为最后的元素可以确定位置,而忽略前面元素的影响

    例如 0 10 0 11

    我们逆序来插入值     11   ?

    ?代表还有空

    11插入后,那个节点记录表示空位是0

    所以插入0 10的时候会通过判断来往?的位置走~

    差不多,自己手想一下就好

    void build()
    {
        int s;
        for(s = 1;s < n;s <<= 1);
        //之所以要出来大于n画一画就好了,n那一层就算没有占满,也要开一层下面的吧
    
        for(int i = s;i < 2 * s;i++)//叶子
        {
            tree[i].l = tree[i].r = i - s + 1;
            tree[i].cnt = 1;
        }
    
        for(int i = s-1;i >= 1;i--)//根
        {
            tree[i].l = tree[2*i].l;
            tree[i].r = tree[2*i+1].r;
            tree[i].cnt = tree[2*i].cnt + tree[2*i+1].cnt;
        }
    }
    

     这是第一种建树,也可以常规来

    void build(int rt, int left, int right)
    {
        tree[rt].l = left;
        tree[rt].r = right;
        tree[rt].cnt = right - left + 1;
        if(left == right)
            return;
        int mid = (left + right) >> 1;
        build(rt<<1,left,mid);
        build(rt<<1|1,mid + 1,right);
    }
    

     code......

    //****单点更新线段树
    #include <cstdio>
    #include <algorithm>
    #include <string.h>
    #define inf (1 << 29)
    using namespace std;
    const int maxn = 2e5 + 100;
    struct node
    {
        int pos;
        int num;
    }p[maxn];
    struct node2{
        int l,r,cnt;
    }tree[maxn << 2];
    int n,ans[maxn];
    void build(int rt, int left, int right)
    {
        tree[rt].l = left;
        tree[rt].r = right;
        tree[rt].cnt = right - left + 1;
        if(left == right)
            return;
        int mid = (left + right) >> 1;
        build(rt<<1,left,mid);
        build(rt<<1|1,mid + 1,right);
    }
    /*void build()
    {
        int s;
        for(s = 1;s < n;s <<= 1);
        //之所以要出来大于n画一画就好了,n那一层就算没有占满,也要开一层下面的吧
    
        for(int i = s;i < 2 * s;i++)//叶子
        {
            tree[i].l = tree[i].r = i - s + 1;
            tree[i].cnt = 1;
        }
    
        for(int i = s-1;i >= 1;i--)//根
        {
            tree[i].l = tree[2*i].l;
            tree[i].r = tree[2*i+1].r;
            tree[i].cnt = tree[2*i].cnt + tree[2*i+1].cnt;
        }
    }*/
    void insertpoint(int i,int pos,int num)
    {
        if(tree[i].l == tree[i].r)
        {
            ans[tree[i].l] = num;
            tree[i].cnt = 0;
            return;
        }
    
        if(pos <= tree[2*i].cnt)
            insertpoint(2*i,pos,num);
        else
            insertpoint(2*i+1,pos - tree[2*i].cnt,num);
        tree[i].cnt--;
    }
    int main()
    {
        while(~scanf("%d",&n))
        {
            int k = 0;
            for(k = 1;k < n;k <<= 1);
            build(1,1,k);
            for(int i = 0;i < n;++i)
                scanf("%d%d",&p[i].pos,&p[i].num);
            for(int i = n - 1;i >= 0;i--)
                insertpoint(1,p[i].pos + 1,p[i].num);
            for(int i = 1;i <= n;i++)
            {
                printf("%d%c",ans[i],i == n ? '
    ' : ' ');
            }
        }
    
        return 0;
    
    }
    
  • 相关阅读:
    kafka 生产者消费者 api接口
    湖南省第九届大学生计算机程序设计竞赛 Interesting Calculator
    Debugger DataSet 调试时查看DataSet
    DELPHI 常用虚拟键:VK_
    DBGRID控件里可以实现SHIFT复选吗?怎么设置?
    在dbgrid中如何多行选中记录(ctl与shift均可用)
    如何在DBGrid里实现Shift+“选择行”区间多选的功能!
    按着shift键对dbgrid进行多条记录选择的问题(50分)
    Delphi实现DBGrid Shift+鼠标左键单击 多选
    Delphi定位TDataSet数据集最后一条记录
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/9442710.html
Copyright © 2011-2022 走看看