小朋友排队
时间限制:1.0s 内存限制:256.0MB
问题描述
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
输入格式
输入的第一行包含一个整数n,表示小朋友的个数。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
输出格式
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
样例输入
3
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。
易知对于一个小朋友而言,知道左边比他高的,知道右边比他矮的,就是他需要移动的次数。维护一个次数的等差数列的前缀和就是答案。
树状数组能解决前面有多少个数比他小的问题,那么前i个数,有x个比他小,比他大的数就是i-1-x,i-1就是减去数本身。然后将数组倒叙加入到树状数组中,可以求出后面有多少比他小的数,最后将这两部分加起来,就是每一个小朋友应该移动的次数和,再用等差数列求和公式,得到答案。
代码实现:
/*
Look at the star
Look at the shine for U
*/
#include<bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define sl(x) scanf("%lld",&x)
using namespace std;
const int N = 1e6+5;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
ll c[N];
ll lowbit(ll x)
{
return x&(-x);
}
void add(ll x)
{
while(x < N)
{
c[x]++;
x += lowbit(x);
}
}
ll query(ll x)
{
ll ans = 0;
while(x)
{
ans += c[x];
x -= lowbit(x);
}
return ans;
}
ll s[N],sum[N];
int main()
{
ll n,i,j,k,m;
sl(n);
ll ans = 0;
for(i = 0;i < n;i++)
{
sl(s[i]);
add(s[i]+1);
sum[i] += (i+1-query(s[i]+1));
}
memset(c,0,sizeof(c));
for(i = n-1;i >= 0;i--)
{
add(s[i]+1);
sum[i] += query(s[i]);
}
for(i = 0;i < n;i++)
ans += (sum[i]*sum[i]+sum[i])/2;
printf("%lld\n",ans);
return 0;
}