zoukankan      html  css  js  c++  java
  • POJ2828---线段树与逆序数&&DUTOJ1210---逆序对构造排列

    来看这样一道问题:http://acm.dlut.edu.cn/problem.php?id=1210

    题目大意:对于一个1-n的排列,a1,a2,a3,a4...an我们把满足i < j,ai > aj这样的数对(ai,aj)成为一个逆序对,另有一个数组b【i】记录aj = i这样的逆序对的个数,例如排列:

    3 1 5 2 4,对应的逆序数组b[1] = 1,b2[2] = 2,b[3] = 0,b[4] = 1,b[5] = 0;换句话说,b【i】代表了i之前有多少比它大的数;

    问:给出一个b数组,构造出一个符合情况的排列a,1 <=n<=100000;

    容易看出,最后的排列一定是唯一的,先来想一想最暴力的解法,其实如果不是因为n的大小是十万级别的话,很容易想到暴力插入的方式,那就是先让大的占位置;

    (1)b【5】= 0:先把5放在排列开头:5;

    (2)b【4】= 1:4前面比4大的有一个:5,4;

    (3)b【3】= 0,3前面比3大的有0,显然:3,5,4;

    (4)b【2】= 2:比2大的有2个:3,5,2,4;

    (5)b【1】= 1:比1大的有3,1,5,2,4;这就是最终的结果;

    按照这种方式插入很定可以构造出最终结果,显然插入一个数时要移动数据元素,最终的复杂度是o(n^2)的,肯定会超时;

    同时我们来看POJ2828:

    Description

    n个人排队,输入第一行为n的值,接下来输入n行,每行一个x值,一个val值,x代表第i个人插入的位置前面有多少个人,i从1到n;

    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

    这两个题几乎就是一个题嘛,对于这种插队问题,需要离线来做;
    因为先插进来的人会影响到后面的人,但是对于最后一个插队的人来说如果他前面有x个人,最终他肯定就在x + 1的位置上。所以需要离线处理并且逆序进行插入;
    我们用一个sum数组表示位置i有没有空位即能不能插进来一个人,sum【i】 = 1代表这个位置上能插进来一个人,sum = 0表示不能插入;
    以poj2828的第一组样例为例:
    注意sum下标从0开始
    sum:1 1 1 1 1表示5个位置上都可以插入,如果我们把(2,69)插入:
    sum:1 1 0 1 1那么肯定第三个位置上的数字是69;再插入(1,33),
    注意到33插在69的前面,实际上当33插入的时候,69还没插入,所以69和33根本互不影响,所以33肯定也是插在第1个位置
    sum:1 0 0 1 1,我们再插入(1,51),这里就怪了,sum【1】貌似已经被33占据了,那么怎么处理51呢?实际上我们知道,在插入51时,33和69都没进来呢,所以可以暂时认为sum【1】和sum【2】都不存在,
    显然51应该插入在第1个1上面,即sum【3】:
    sum:1 (0 0) 0 1;注意这里括号括起来的是咱们假想的,真正插入51的时候,33和69还没插进呢,所以51确实插入了第“1”个位置,因为在33,69没插进来时,前面只有一个第0个位置有空。
    所以这里插入的原则就很清楚了,对于一个(x,value),应该插入到第x个1的位置,怎么统计位置i前面有几个1呢??那不正是统计sum的前缀和嘛?怎么统计区间和?线段树嘛!

    插入(x,value)时,线段树把一个区间分为一个左子区间和一个右左区间,如果左区间还有x个空间可以插入,就应该到左子区间去更新;否则应该到右子区间去查询,但是此时,查询的位置应该减去
    sum【rson】用以表示相对的位置,因为sum里面保存的是区间之内有几个位置可以插入,所以进入右子区间时,应该减掉在左子区间里的那些“1”的个数,所以这里用的是相对位置;
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #define INF 100000000
    #define LL (0x3f3f3f3f3f3f3f3f)*2
    #define lc rt<<1
    #define rc rt<<1|1
    using namespace std;
    const int maxn = 2e5 + 5;
    typedef long long ll;
    
    int pos[maxn], val[maxn];
    int sum[maxn << 2];
    int ans[maxn];
    int n;
    
    void pushup(int rt)
    {
        sum[rt] = sum[lc] + sum[rc];
    }
    void build(int rt, int l, int r)
    {
        if(l == r) {
            sum[rt] = 1;
            return;
        }
        int m = (l + r) >> 1;
        build(lc, l, m);
        build(rc, m + 1, r);
        pushup(rt);
    }
    void update(int p, int add, int l , int r , int rt ) {
        if(l == r) {
            sum[rt] = 0;
            ans[l] = add;
            return ;
        }
        int m = (l + r) >> 1;
        if(p <= sum[rt << 1])update(p, add, l,m,lc);
        else update(p - sum[rt << 1], add, m+1,r,rc);
        pushup(rt);
    }
    int main()
    {
        for(;~scanf("%d", &n);)
        {
            for(int i = 1; i <= n; ++i)
                scanf("%d%d", pos + i, val + i);
            build(1, 1, n);
            for(int i = n; i >= 1; --i)
            {
                int p = pos[i]+1;
                int v = val[i];
                update(p, v, 1, n, 1);
            }
            for(int i = 1; i <= n; ++i)
                printf("%d%c", ans[i], (i ^ n ? ' ' : '
    '));
            memset(ans, 0, sizeof(ans));
        }
    }
  • 相关阅读:
    leetcode 子集
    leetcode 1111. 有效括号的嵌套深度
    leetcode289 生命游戏
    关于三次握手和四次挥手,发送方和接收方处于的状态的问题。
    面试题:随意取数
    2020/3/31
    01背包和完全背包
    USACO Agri-Net 3.1
    mfc小计
    static 成员小记
  • 原文地址:https://www.cnblogs.com/Norlan/p/5017108.html
Copyright © 2011-2022 走看看