时间限制: 1Sec 内存限制: 128MB 提交: 75 解决: 11
题目描述
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
样例说明
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
数据规模和约定
对于100%的数据,1< =n< =100000,0< =Hi< =1000000。
每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
样例说明
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
数据规模和约定
对于100%的数据,1< =n< =100000,0< =Hi< =1000000。
输入
输入的第一行包含一个整数n,表示小朋友的个数。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
输出
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
样例输入
3 3 2 1
样例输出
9
#include<cstdio> #define maxn 100010 struct data { int num,cnt; }A[maxn],temp[maxn]; int n; //long long ans; long long ANS[100]; void merge(int L1,int R1,int L2,int R2) { int k=0,i=L1,j=L2; while(i<=R1 && j<=R2) { if(A[i].num<=A[j].num)
//如果A[i].num<A[j].num,则A[L2,j-1]的元素小于A[i],如果相等,则先存储上数组的元素,这个时候下数组的A[L2,j-1]仍然都满足小于A[i].num { A[i].cnt+=j-L2; temp[k++]=A[i++]; } else if(A[i].num>A[j].num)//A[i,R1]都大于A[j] { A[j].cnt+=R1-i+1; temp[k++]=A[j++]; } } //剩下上数组,那么剩下的元素都要移动下数组的元素个数 while(i<=R1) { A[i].cnt+=R2-L2+1; temp[k++]=A[i++]; } while(j<=R2) temp[k++]=A[j++]; //剩下下数组,就不需要移动 for(i=0;i<k;i++) A[L1+i]=temp[i]; } void mergeSort(int L,int R) { if(L<R) { int mid = (L+R)/2; mergeSort(L,mid); mergeSort(mid+1,R); merge(L,mid,mid+1,R); } } int main(void) { //FILE *in = fopen("input10.txt","r"); //fscanf(in,"%d",&n); //for(int i=0;i<n;i++) fscanf(in,"%d",&(A[i].num)); scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&(A[i].num)); mergeSort(0,n-1); for(int i=0;i<n;i++) { long long s = A[i].cnt; //ans+=s*(s+1)/2; ANS[1]+=s*(s+1)/2; int length=1; while(ANS[length]/10!=0) { ANS[length+1]+=ANS[length]/10; ANS[length]%=10; length++; } if(ANS[0]<length) ANS[0]=length; } //printf("%lld",ans); for(int i=ANS[0];i>=1;i--) printf("%d",ANS[i]); //printf(" "); return 0; }
大致的思路:使用归并排序的思想求逆序对的思想,划分成为一个上数组,一个数组。
上数组的元素往下数组方向移动,下数组的元素往上数组方向移动。
上数组的元素往下数组方向移动,移动的长度是下数组中小于该元素的个数;
下数组的元素往桑数组方向移动,移动的长度是上数组中大于该元素的个数。
在结构体中记录移动的次数。
由于数据较大,可以使用高精度加法进行计算。
注意:这里使用long long 也是可以的
#include<cstdio> #define maxn 100010 struct data { int num,cnt; }A[maxn],temp[maxn]; int n; long long ans; long long ANS[100]; void merge(int L1,int R1,int L2,int R2) { int k=0,i=L1,j=L2; while(i<=R1 && j<=R2) { if(A[i].num<=A[j].num)//如果A[i].num<A[j].num,则A[L2,j-1]的元素小于A[i],如果相等,则先存储上数组的元素,这个时候下数组的A[L2,j-1]仍然都满足小于A[i].num { A[i].cnt+=j-L2; temp[k++]=A[i++]; } else if(A[i].num>A[j].num)//A[i,R1]都大于A[j] { A[j].cnt+=R1-i+1; temp[k++]=A[j++]; } } //剩下上数组,那么剩下的元素都要移动下数组的元素个数 while(i<=R1) { A[i].cnt+=R2-L2+1; temp[k++]=A[i++]; } while(j<=R2) temp[k++]=A[j++]; //剩下下数组,就不需要移动 for(i=0;i<k;i++) A[L1+i]=temp[i]; } void mergeSort(int L,int R) { if(L<R) { int mid = (L+R)/2; mergeSort(L,mid); mergeSort(mid+1,R); merge(L,mid,mid+1,R); } } int main(void) { FILE *in = fopen("input10.txt","r"); fscanf(in,"%d",&n); for(int i=0;i<n;i++) fscanf(in,"%d",&(A[i].num)); //scanf("%d",&n); //for(int i=0;i<n;i++) scanf("%d",&(A[i].num)); mergeSort(0,n-1); for(int i=0;i<n;i++) { long long s = A[i].cnt; ans+=s*(s+1)/2; } printf("%lld",ans); return 0; }