历届试题 小朋友排队
时间限制:1.0s 内存限制:256.0MB
问题描述
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
输入格式
输入的第一行包含一个整数n,表示小朋友的个数。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
输出格式
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
样例输入
3
3 2 1
3 2 1
样例输出
9
样例说明
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
数据规模和约定
对于10%的数据, 1<=n<=10;
对于30%的数据, 1<=n<=1000;
对于50%的数据, 1<=n<=10000;
对于100%的数据,1<=n<=100000,0<=Hi<=1000000。
对于30%的数据, 1<=n<=1000;
对于50%的数据, 1<=n<=10000;
对于100%的数据,1<=n<=100000,0<=Hi<=1000000。
思路:
首先我们来分析一下这组数据。
4
1 9 5 2
那么对于第1的元素,它左边没有比它大的,右边没有比它小的,所以它不需要移动
第二个元素9,因为右边有一个5比它小,所以9需要移动1一次。
第三个元素5,左边有一个数9比它大,右边有一个数2比它小,所以它需要移动 两次。
而最后一个元素2,因为左边有两个元素比它大,所以它需要移动两次。
那么我们可以观察出规律。
对于第i个数,当移动次数最优的时候,它需要移动的次数就等于左边比它大的数的个数,和右边比它小的数的个数的总和。
那么我们知道这个规律后,我们该如何解决这个问题呢?
上面提到的规律中涉及到逆序对的问题,那么在n<=1e5的数据范围条件下,n*n来求每一个元素的逆序对显然会TLE的。
那么我们就要用到树状数组求依序对。
如果不知道树状数组这个数据结构,可以自行百度学习。
这里会用到最简单的树状数组的模板。
我们知道,对于树状数组,query( a[i] ) 等于 1~a[i]的前缀和
我们从左到右遍历数组的时候,对于第i个元素,当前已经加入了i个元素(包括自己)。那么调用query函数求1~a[i]的前缀和后,
i - a[i] 就等于在前面i-1个中,有多少个数比a[i] 大,因为如果比a[i]小,就会被算在query( a[i] ) 中。
而前面的数比后面的数大就是我们要找的逆序对,即左边比它大的数的个数。
那么右边同理:
我们只需要从右向左扫求每一个数a[i] 的前缀和即代表这第i个数后面有多少个数比它小。
然后对应求一个每一个元素的移动次数的愤怒值,加在一起就是答案了。
注意树状数组不能对0位置加数,
而输入数据的身高有0,所以我们对数据的每一个身高都加1,这样并不会影响结果。(因为考虑的都是相对大小)
细节见代码:(满分)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #include <vector> #define sz(a) int(a.size()) #define all(a) a.begin(), a.end() #define rep(i,x,n) for(int i=x;i<n;i++) #define repd(i,x,n) for(int i=x;i<=n;i++) #define pii pair<int,int> #define pll pair<long long ,long long> #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define MS0(X) memset((X), 0, sizeof((X))) #define MSC0(X) memset((X), '\0', sizeof((X))) #define pb push_back #define mp make_pair #define fi first #define se second #define eps 1e-6 #define gg(x) getInt(&x) #define db(x) cout<<"== "<<x<<" =="<<endl; using namespace std; typedef long long ll; inline void getInt(int* p); const int maxn=1000010; const int inf=0x3f3f3f3f; /*** TEMPLATE CODE * * STARTS HERE ***/ int n; ll a[maxn]; ll b[maxn]; ll tree[maxn]; #define lowbit(x) (x)&(-x) void add(int i) { while(i<=maxn) { tree[i]+=1; i+=lowbit(i); } } ll query(int i) { ll res=0; while(i>0) { res+=tree[i]; i-=lowbit(i); } return res; } ll num[maxn]; int main() { gbtb; cin>>n; repd(i,1,n) { cin>>a[i]; a[i]++; } ll tot=0ll; repd(i,1,n) { add(a[i]); tot=i-query(a[i]);// 统计当前序列中大于a的元素的个数 num[i]+=tot; } MS0(tree); for(int i=n;i>=1;i--) { add(a[i]); tot=query(a[i]-1); num[i]+=tot; } ll ans=0ll; repd(i,1,n) { // cout<<i<<" "<<num[i]<<endl; ans+=(num[i]*(1ll+num[i]))/2ll; } cout<<ans<<endl; return 0; } inline void getInt(int* p) { char ch; do { ch = getchar(); } while (ch == ' ' || ch == '\n'); if (ch == '-') { *p = -(getchar() - '0'); while ((ch = getchar()) >= '0' && ch <= '9') { *p = *p * 10 - ch + '0'; } } else { *p = ch - '0'; while ((ch = getchar()) >= '0' && ch <= '9') { *p = *p * 10 + ch - '0'; } } }