HDU 5592 ZYB's Premutation (线段树求逆序对)

题意
给你一组从1到n的排列,表示的是你当前所拥有的逆序对数,现在让你重新还原这个排列。
思路
我们知道 [ 1 · · · i ] 区间和 [ 1 · · · · i 1 ] 区间的逆序对数了,那我们其实就可以确定第i个位置的大小是多少了,因为他们的差(设为p)其实就表明当前位置 i 的这个数在他的前面有p个比他大,那么换句话说他其实就是他在当前区间是第p+1大的数字,那么就变成简单的线段树维护当前区间有几个数字这种问题了,具体怎么做,就是我们维护区间和,表示的是当前区间有多少个数字,每次查找区间第几大的时候我们先看他的右子树,优先往右子树走,如果当前要找第k大,如果右子树区间有k个那么就走右子树,小于k个,那么就取查左子树的(k-右子树现有的区间个数),之后找到之后把这个值变成0,pushup一下就好了
代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+10;
int a[maxn],A;
struct seg
{
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    int tree[maxn<<2];
    void pushup(int rt)
    {
        tree[rt] = tree[rt<<1] + tree[rt<<1|1];
    }
    void build(int l,int r,int rt)
    {
        if(l == r)
        {
            tree[rt] = r - l + 1;
            return ;
        }
        int m = (l+r)>>1;
        build(lson);
        build(rson);
        pushup(rt);
    }
    void update(int pos,int l, int r,int rt)
    {
        if(l == r)
        {
            A = l;
            tree[rt] = 0;
            return ;
        }
        int m = (l+r)>>1;
        if(tree[rt<<1] >= pos) update(pos,lson);
        else update(pos-tree[rt<<1],rson);
        pushup(rt);
    }
};
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        seg tree;
        vector<int>ans; 
        int n;
        scanf("%d",&n);
        tree.build(1,n,1);
        for(int i = 0 ; i < n ; i++) scanf("%d",&a[i]);
        int cnt = 0;
        for(int i = n - 2 ; i >= 0 ; i --)
        {
            int p = a[i+1] - a[i];
            tree.update(n-p-cnt,1,n,1); // 我这里写的是求第k大其实就是求第n-k小之后优先查左子树就好了
            cnt++;
            ans.push_back(A);
        }
        tree.update(1,1,n,1);
        ans.push_back(A);
        for(int i = ans.size()-1; i >=0 ; i--)
        {
            printf("%d%c",ans[i]," \n"[i == 0]);
        }
    }
}
/*
100
6
0 1 2 5 7 8
*/

猜你喜欢

转载自blog.csdn.net/wjmwsgj/article/details/82711673
今日推荐