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

可以通过这个序列得到前面比自己小的数,进而知道自己正常情况下排到的位置。但是如果后面有数已经占到了这个位置,就要相应地往后面排。用树状数组+二分 找到有空的那个位置。从后往前扫。

代码:

#pragma warning(disable:4996)  
#include <iostream>  
#include <algorithm>  
#include <cmath>  
#include <vector>  
#include <string>  
#include <cstring>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;

int n;
int val[50005];
int ans[50005];
int wc[50005];

int lowbit(int x)
{
	return x&(-x);
}

void add(int x,int y)
{
	while (x <= n)
	{
		ans[x] = ans[x] + y;
		x = x + lowbit(x);
	}
}

int sum(int x)
{
	int res = 0;
	while (x > 0)
	{
		res += ans[x];
		x = x - lowbit(x);
	}
	return res;
}

int cal(int x)
{
	int ri = n;
	int le = 1;
	int mid;

	while (le <= ri)
	{
		mid = (le + ri) >> 1;
		if (sum(mid) >= x)
		{
			ri = mid - 1;
		}
		else
		{
			le = mid + 1;
		}
	}
	return le;
}

void solve()
{
	int i;
	for (i = 1; i <= n; i++)
	{
		add(i, 1);
	}

	for (i = n; i >= 1; i--)
	{
		int pos = i - val[i] - 1;//前面有多少比它小的数
		wc[i] = cal(pos + 1);
		add(wc[i], -1);

	}
	for (i = 1; i <= n; i++)
	{
		if (i == 1)
			printf("%d", wc[i]);
		else
			printf(" %d", wc[i]);
	}
	printf("\n");
}
int main()
{
	//freopen("i.txt", "r", stdin);
	//freopen("o.txt", "w", stdout);

	int t;
	int i;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		memset(ans, 0, sizeof(ans));
		for (i = 1; i <= n; i++)
		{
			scanf("%d", val + i);
		}
		for (i = n; i >= 1; i--)
		{
			val[i] = val[i] - val[i - 1];
		}
		solve();
	}
	//system("pause");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值