蓝桥杯 历届试题 小朋友排队

这道题目有两种做法求逆序数,一是归并排序,而是线段树或树状数组,两种都实现了一下。

这道题还需要对数据进行离散化,注意的地方写在注释里了。


归并排序



<span style="font-size:18px;">#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAX 100010
int a[MAX];
int b[MAX];
struct node{
	int x;	//位置 
	int h;	//大于 
	int l;	//小于 
}num[MAX];
int n;

int find(int x);
void merge(int l,int r);

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i] = a[i];
		num[i].h = 0;	//必须置零 
	}
	sort(b+1,b+n+1);
	//离散化 
	for(int i=1;i<=n;i++){
		int k = find(a[i]);
		a[i] = k;
		b[k]--;	//保证后边与他相同的数不受影响,因为查找最靠左的数,不会影响前边的数 
		num[a[i]].x = i;	//保存离散后的数的原位置 
	}
	merge(1,n);
	long long ans = 0;
	for(int i=1;i<=n;i++){
		num[i].l = i - (num[i].x - num[i].h);	//数字的排列位置-(数字位置-大于该数字的数) 
		ans += (long long)(num[i].l + num[i].h) * (num[i].l + num[i].h + 1)/2; //最终错误点,强制类型转换,否则会只得70%的分 
	}
	printf("%I64d\n",ans);
	return 0;
}
//归并排序 
void merge(int l,int r){
	if(l==r)	return ;
	int mid = (l+r)/2;
	merge(l,mid);
	merge(mid+1,r);
	int i = l,j = mid + 1,k = 0;
	while(i<=mid&&j<=r){
		if(a[i]<=a[j]){	//注意 = 
			b[k++] = a[i++];
		}
		else{
			b[k++] = a[j];
			num[a[j++]].h += mid - i + 1;	//计算该节大于a[j]的数   注意+= 
		}
	}
	while(i<=mid){
		b[k++] = a[i++];
	}
	while(j<=r){
		b[k++] = a[j++];
	}
	for(int p=l,q = 0;p<=r;p++,q++){
		a[p] = b[q];
	}
}


//对b二分查找 
int find(int x){
	int l = 1,r = n,mid;
	while(l<r){
		mid = (l+r)/2;
		if(b[mid]==x){
			if(b[mid-1]<x){
				return mid;
			}
			else{
				r = mid-1;
			}
		}
		else if(b[mid]>x){
			r = mid-1;
		}
		else{
			l = mid + 1;
		}
	}
	return l;
}</span>

线段树做法,插入和计算合并用了一个函数,即线段树+离散化


<span style="font-size:18px;">#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<vector>
#include<list>
#include<algorithm>
using namespace std;
#define MAX 100009
#define lson l,m,level*2
#define rson m+1,r,level*2+1
int a[MAX];
int b[MAX];
int insert(int l,int r,int level,int x);
int found(int x);
struct node{
	int x;
	int l;
	int h;
}num[MAX];
	int n;
	int tree[MAX*5];
int main(){

	scanf("%d",&n);
	 for(int i=1;i<=n;i++){
	 	scanf("%d",&a[i]);
	 	b[i] = a[i];
	 	num[i].h = 0;
	 }
	 //离散化 
	 sort(b+1,b+1+n);
	 for(int i=1;i<=n;i++){
	 	int t = found(a[i]);
	 	a[i] = t;
	 	num[t].x = i;
	 	b[t]--;
	 }
	 //线段树插入 
	 memset(tree,0,sizeof(tree));
	 long long sum = 0;
	 for(int i=1;i<=n;i++){
	 	num[a[i]].h = insert(1,n,1,a[i]);	//点插入(按照原顺序,计算比该点大的数的数目) 
	 	num[a[i]].l = a[i] - (num[a[i]].x - num[a[i]].h);
	 	sum += (long long)(num[a[i]].h + num[a[i]].l)*(num[a[i]].h + num[a[i]].l + 1)/2;
	 } 
	 printf("%I64d\n",sum);
	return 0;
}

int insert(int l,int r,int level,int x){
	if(l==r&&l==x){
		tree[level] = 1;
		return 0;	//注意该点不计入 
	}
	int m = (l+r)>>1;
	int temp;
	if(x<=m){
		temp = insert(lson,x);
		temp += tree[level*2+1];	//加上比他大的数 
	}
	else if(x>m){
		temp = insert(rson,x);
	}
	tree[level] = tree[level*2] + tree[level*2+1];	//更新tree 
	return temp;
}
//二分 
int found(int x){
	int l = 1,r = n;
	int mid;
	while(l<r){
		mid = (l+r)/2;
		if(b[mid]==x){
			if(b[mid-1]!=x)	return mid;
			else	r = mid-1;
		}
		else if(b[mid]>x){
			r = mid-1;
		}
		else if(b[mid]<x){
			l = mid+1;
		}
	}
	return l;
}</span>



你可能感兴趣的:(归并排序,蓝桥杯)