hdu5592/BestCoder Round #65 树状数组寻找第K大

ZYB's Premutation

 
 
 
 Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYBZYB有一个排列PP,但他只记得PP中每个前缀区间的逆序对数,现在他要求你还原这个排列.

(i,j)(i < j)(i,j)(i<j)被称为一对逆序对当且仅当A_i>A_jAi​​>Aj​​
输入描述
第一行一个整数TT表示数据组数。

接下来每组数据:

第一行一个正整数NN,描述排列的长度.

第二行NN个正整数A_iAi​​,描述前缀区间[1,i][1,i]的逆序对数.

数据保证合法.

1 \leq T \leq 51T5,1 \leq N \leq 500001N50000
输出描述
TT行每行NN个整数表示答案的排列.
输入样例
1
3
0 1 2
输出样例
3 1 2

 题解:设f_ifi​​是第ii个前缀的逆序对数,p_ipi​​是第ii个位置上的数,则f_i-f_{i-1}fi​​fi1​​是ii前面比p_ipi​​大的数的个数.我们考虑倒着做,当我们处理完ii后面的数,第ii个数就是剩下的数中第f_i-f_{i-1}+1fi​​fi1​​+1大的数,用线段树和树状数组可以轻松地求出当前第kk个是11的位置,复杂度O(N \log N)O(NlogN).

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 5e4 + 500;
long long arr[maxn],b[maxn];

#define lowbit(x) ((x)&(-x))

struct BinaryIndexTree
{
    int val[maxn],sz;

    void init(int sz){
        this->sz=sz;
        memset(val , 0 , sizeof(int)*(sz+5));
    }

    void updata(int pos ,int key){
        while(pos<=sz){
            val[pos]+=key;
            pos+=lowbit(pos);
        }
    }

    int prefixsum(int pos){
        int res=0;
        while(pos>0){
            res+=val[pos];
            pos-=lowbit(pos);
        }
        return res;
    }

    int query(int l,int r){
        return prefixsum(r)-prefixsum(l-1);
    }

    //找到第一个大于等于k的位置返回
    //若不存在,返回-1
    int lower_bound(int k){
        if(prefixsum(sz)<k) return -1;
        int l = 1 , r = sz;
        while(l <= r){
            int mid = l + ((r-l)>>1);
            if(prefixsum(mid) < k) l = mid + 1;
            else r = mid - 1;
        }
        return l;
       }
    
}solver;

int ans[maxn];

int main(int argc,char *argv[]){
    int Case;
    scanf("%d",&Case);
    while(Case--){
        int n;
        scanf("%d",&n);
        solver.init(n);
        for(int i = 1 ; i <= n ; ++ i) scanf("%I64d",arr+i);
        for(int i = 1 ; i <= n ; ++ i) b[i] = arr[i] - arr[i-1];
        for(int i = 1 ; i <= n ; ++ i) solver.updata(i , 1);
        for(int i = n ; i >= 1 ; -- i){
            long long rank = i - b[i];
            int t = solver.lower_bound(rank);
            ans[i] = t;
            solver.updata(t,-1);
        }
        printf("%d",ans[1]);
        for(int i = 2 ; i <= n ; ++ i) printf(" %d",ans[i]);
        printf("\n");
    }
    return 0;
}
代码

 

二分法

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+20, M = 30005, mod = 1e9+7, inf = 0x3f3f3f3f;
typedef long long ll;
//不同为1,相同为0

int n;
struct BIT {
    int tree[N] ;

    int lowbit(int x) {
        return x&(-x);
    }
    void add(int x, int add, int n) {
        for (; x <= n; x += lowbit(x)) {
            tree[x] += add;
        }
    }
    int sum(int x) {
        int s = 0;
        for (; x > 0; x -= lowbit(x)) {
            s += tree[x];
        }
        return s;
    }
    void clears() {
        memset(tree, 0, sizeof tree);
    }
}Bit;
int cal(int x) {
    int l = 1, r = n, ret = -1;
    while(l<=r) {
        int mid = (l+r)>>1;
        int G = Bit.sum(n) - Bit.sum(mid-1);
        if(G>=x) l = mid+1, ret = mid;
        else r = mid-1;
    }
    return ret;
}
int main() {
    int T,b[N],a[N],ans[N];
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i] = a[i] - a[i-1];
        Bit.clears();
        for(int i=1;i<=n;i++) Bit.add(i,1,n);
        int pos = n - b[n];
        ans[n] = pos;
        Bit.add(pos,-1,n);
        for(int i = n-1;i >= 1;i--) {
            pos = cal(b[i]+1);
            ans[i] = pos;
            Bit.add(pos,-1,n);
        }
        for(int i=1;i<n;i++) {
            printf("%d ",ans[i]);
        }
        printf("%d\n",ans[n]);
    }
    return 0;
}

 

posted @ 2016-03-24 23:00  meekyan  阅读(208)  评论(0编辑  收藏  举报