您的位置:首页 > 其它

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

2016-04-21 17:23 288 查看
这道题目有两种做法求逆序数,一是归并排序,而是线段树或树状数组,两种都实现了一下。

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

归并排序

<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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: