hdu 5592 ZYB's Premutation (线段树)

ZYB's Premutation

 
 Accepts: 218
 
 Submissions: 983
 Time Limit: 2000/1000 MS (Java/Others)
 
 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

思路:
稍微转换一下a[i] - a[i - 1],就变成了经典的线段树的题目了
/*======================================================
# Author: whai
# Last modified: 2015-12-07 12:38
# Filename: c.cpp
======================================================*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <stack>

using namespace std;

#define LL __int64
#define PB push_back
#define P pair<int, int>
#define X first
#define Y second

#define LS (2 * n)
#define RS (2 * n + 1)

const int N = 5 * 1e4 + 5;

int a[N];
int ans[N];

struct Seg {
	int l, r, size;
}seg[4 * N];

void up(int n) {
	seg[n].size = seg[LS].size + seg[RS].size;
}

void build(int l, int r, int n) {
	seg[n].l = l; seg[n].r = r;
	if(l == r) {
		seg[n].size = 1;
		return ;
	}
	int mid = (l + r) >> 1;
	build(l, mid, LS);
	build(mid + 1, r, RS);
	up(n);
}

int get_id(int rank, int n) {
	if(seg[n].l == seg[n].r) return seg[n].l;
	if(rank <= seg[LS].size) {
		return get_id(rank, LS);
	} else {
		rank -= seg[LS].size;
		return get_id(rank, RS);
	}
}



void update(int p, int n) {
	if(seg[n].l == seg[n].r) {
		--seg[n].size;
		return ;
	}
	int mid = (seg[n].l + seg[n].r) >> 1;
	if(p <= mid) update(p, LS);
	else update(p, RS);
	up(n);
}

void gao(int n) {
	for(int i = n - 1; i > 0; --i) {
		a[i] -= a[i - 1];
	}
	for(int i = 0; i < n; ++i) {
		a[i] = i + 1 - a[i];
	}
	//for(int i = 0; i < n; ++i) {
	//	cout<<a[i]<<' ';
	//}
	//cout<<endl;
	
	build(1, n, 1);

	for(int i = n - 1; i >= 0; --i) {
		ans[i] = get_id(a[i], 1);
		update(ans[i], 1);
	}

	for(int i = 0; i < n; ++i) {
		printf("%d%c", ans[i], i == n - 1 ? '\n' : ' ');
	}
}

int main() {
	int T;
	scanf("%d", &T);
	while(T--) {
		int n;
		scanf("%d", &n);
		for(int i = 0; i < n; ++i) {
			scanf("%d", &a[i]);
		}
		gao(n);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值