BestCode_Round65_C题(线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5592

思路:很容易处理出当前位置前面比当前位置的数大的数的个数。假设前缀逆序对数为num[i] , 则num[i]-num[i-1]就为位置i前面比原本在位置i上的数大的个数。

假设最后一个数为N. 则[1-N]中比位置N更大的数为num[N]-num[N-1] ,则第N个位置的数就为 N - (num[N]-num[N-1]).(在[1,N]中第N - (num[N]-num[N-1])大的数)

第N-1个位置的数为 除去第N个位置已经求出来的数外在[1,N]中第(N-1) - (num[N-1]-num[N-2])大的数(这个数用线段树求解)


#include <iostream>
#include <cstdio>
#include <algorithm>
#define bug1 printf("bug1\n")
#define lson rt<<1
#define rson rt<<1|1
using namespace std;

const int maxx = 50010;
struct stree{
    int l;
    int r;
    int val;
}a[maxx*4];

void push_up(int rt){
    a[rt].val = a[lson].val + a[rson].val;
}

void build(int rt,int l,int r){
    a[rt].l = l;
    a[rt].r = r;
    if(l == r){
        a[rt].val = 1;
        return ;
    }
    int mid = (l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    push_up(rt);
}

void update(int rt,int k){
    if(a[rt].l == a[rt].r){
        a[rt].val = 0;
        return ;
    }
    int mid = (a[rt].l + a[rt].r)>>1;
    if(k<=mid) update(lson,k);
    else update(rson,k);
    push_up(rt);
}

int query(int rt,int k){
   if(a[rt].l==a[rt].r){
        return a[rt].l;
   }
   int mid = (a[rt].l+a[rt].r)>>1;
   if(a[lson].val>=k) return query(lson,k);
   else return query(rson,k-a[lson].val);
}

int T, N;
int ans[maxx];
int num[maxx];

int main( ){
    cin>>T;
    num[0] = 0;
    while(T--){
        cin>>N;
        build(1,1,N);
        for(int i = 1;i <= N; ++i){
            scanf("%d",&num[i]);
        }
        for(int i = N;i >= 1; --i){
            num[i]=num[i]-num[i-1];
        }
        for(int i = N; i >= 1; --i){
            ans[i] = query(1,i-num[i]);
            update(1,ans[i]);
        }
        for(int i = 1;i < N; ++i){
            printf("%d ",ans[i]);
        }
        printf("%d\n",ans[N]);
    }
    return 0;
}

如有BUG欢迎指出~~~

EMAIL: hh_0828@outlook.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值