HDU5592(线段树+二分)

***********************************************声明******************************************************

      原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。

      由于各种原因,可能存在诸多不足,欢迎斧正!

********************************************************************************************************* 

ZYB's Premutation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 838    Accepted Submission(s): 403


Problem Description
ZYB  has a premutation  P ,but he only remeber the reverse log of each prefix of the premutation,now he ask you to 
restore the premutation.

Pair  (i,j)(i<j)  is considered as a reverse log if  Ai>Aj  is matched.
 

Input
In the first line there is the number of testcases T.

For each teatcase:

In the first line there is one number  N .

In the next line there are  N  numbers  Ai ,describe the number of the reverse logs of each prefix,

The input is correct.

1T5 , 1N50000
 

Output
For each testcase,print the ans.
 

Sample Input
  
  
1 3 0 1 2
 

Sample Output
  
  
3 1 2
 

Source


     本题要求根据每个位置逆序数的累计个数,还原数列。可以从后往前推,求出当前位置元素ans[i]的前面有多少元素比他大或比他小,ans[i]=log[i]-log[i-1,注意边界的处理,但时,只知道前面比他大或比他小的元素是不能准确定位元素大小的,还得知道后面比他大或比他小的元素。

1)、前面比他大或小的元素个数可以通过反向遍历O(n)求出。

2)、后面比他大或小的元素个数可以通过在某个数组中标价,然后通过二分结合某个快速求区间和的算法-线段树得出,时间复杂度O(log(N))^2

     这样一来,总的时间复杂度为O(N*log(N))  .好久没做题,在没有模板的情况下一棵线段树和二分求第一个等于某个元素的代码调试了很久,看来以后得多敲敲这类基础代码,而不是一味的堆积业务代码,哈哈。


#include<cstdio>
#include<cstring>
#define MAXN 50000+10

int log[MAXN];
int ans[MAXN];

struct treeNode
{
	int l,r,sum;
}tree[MAXN*3];

void build(int id,int l,int r)
{
	tree[id].l=l,tree[id].r=r;
	if(l==r)
	{
		tree[id].sum=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(id<<1,l,mid);
	build(id<<1|1,mid+1,r);
	tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}

void update(int pos,int id,int val)
{
	if(tree[id].l==tree[id].r)
	{
		tree[id].sum+=val;
		return ;
	}
	int mid=(tree[id].l+tree[id].r)>>1;
	if(pos<=mid)
	{
		update(pos,id<<1,val);
	}
	else
	{
		update(pos,id<<1|1,val);
	}
	tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}

int query(int l,int r,int id)
{
	if(tree[id].l==l&&tree[id].r==r)
	{
		return tree[id].sum;
	}
	int mid=(tree[id].l+tree[id].r)>>1;
	if(r<=mid)
	{
		return query(l,r,id<<1);
	}
	else if(l>mid)
	{
		return query(l,r,id<<1|1);
	}
	return query(l,mid,id<<1)+query(mid+1,r,id<<1|1);
}

int main()
{
	int t;
	scanf("%d",&t);
	while(--t)
	{
		memset(tree,MAXN*sizeof(int),0);
		int n;
		scanf("%d",&n);
		log[0]=0;
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&log[i]);
		}
		build(1,1,n);
		for(int j=n;j>0;--j)
		{
			int tmp=j-(log[j]-log[j-1])-1;
			int l=1,r=n;
			while(l<r)
			{
				int mid=(l+r)>>1;
			//	printf("tmp=%d  mid=%d l=%d  r=%d\n",tmp,mid,l,r);
				if(query(l,mid,1)<=tmp)
				{
					l=mid+1;
				}
				else
				{
					r=mid;
				}
			}
			ans[j]=l;
		//	printf("%d  \n",l);
			update(ans[j],1,-1);
		}
		for(int k=1;k<=n;++k)
		{
			printf("%d ",ans[k]);
		}
		printf("\n");
	}
	return 0;
}

                           




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值