题目链接:HDOJ5775

分析这个题要求什么:L【i】和R【i】,意思是i出现的所有位置之中,最左边那个的坐标和最右边那个的坐标


题目中给的代码,是任意数字c,c会被其后面比c小的数字各交换一次,之后c就会只向前移动


如何求L【i】,很简单

L【a【i】】=min(i,a【i】)

在1到n的全排列时,对于每个数字来说,在冒泡排序中,结果唯一为1,2,3,……n

所以,每个数字的起始点为a【i】,终点为i,最左边的出现位置必为min(i,a【i】)


那么R【i】呢,需要知道,在原数组中有多少个比R【i】小的数,加上数字a【i】的原来位置,就是R【i】的值

那么,需要用线段树或者树状数组维护:当前数的右边有多少个比自己小的数

逆序先查询,后插入,就可以处理好


代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=1e5+10;
int a[maxn],c[maxn],L[maxn],R[maxn],n;

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

void update(int x,int add){
	while(x<=n){
		c[x]+=add;
		x+=LowBit(x);
	}
}

int getsum(int x){
	int ret=0;
	while(x){
		ret+=c[x];
		x-=LowBit(x);
	}
	return ret;
}

int main(){
	//freopen("input.txt","r",stdin);
	int t;
	scanf("%d",&t);
	for(int Case=1;Case<=t;Case++){
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			c[i]=0;
			L[a[i]]=min(i,a[i]);
		}
		for(int i=n;i>=1;i--){
			R[a[i]]=i+getsum(a[i]);
			update(a[i],1);
		}
		printf("Case #%d: ",Case);
		for(int i=1;i<=n;i++)
			printf("%d%c",R[i]-L[i],i==n?'\n':' ');
	}
	return 0;
}