1. 程式人生 > >歷屆試題 小朋友排隊【樹狀陣列】

歷屆試題 小朋友排隊【樹狀陣列】

  歷屆試題 小朋友排隊  

時間限制: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。

思路:

這一題主要是求每個人被交換的次數,最後用自然數求和公式即可。

我們可以發現一個人被交換的次數=前面大於它的人的個數+後面小於它的人的個數。

這樣就轉化為類似於求逆序數了。

模擬插入排序可以過90%,用樹狀陣列可以過100%,注意相等的情況。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define ll long long
int n,t[MAXN],c[MAXN],dis[MAXN];
struct node
{
    int x,i;
} p[MAXN];
bool cmp(node a,node b)
{
    return a.x < b.x;
}
int lowbit(int i)
{
    return i&(-i);
}
void add(int i,int x)
{
    while(i<=n)
    {
        c[i]+=x;
        i+=lowbit(i);
    }
}
int sum(int i)
{
    int res=0;
    while(i)
    {
        res+=c[i];
        i-=lowbit(i);
    }
    return res;
}
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&p[i].x);
        p[i].i=i;
    }
    memset(dis,0,sizeof dis);
    sort(p+1,p+n+1,cmp);
    for(int i=1; i<=n; i++)
    {
        t[p[i].i]=i;
        if(i>1 && p[i].x==p[i-1].x) t[p[i].i]=t[p[i-1].i];
    }
    long long ans=0;
    memset(c,0,sizeof c);
    for(int i=1; i<=n; i++)
    {
        add(t[i],1);
        ll s=i-sum(t[i]);
        dis[t[i]]++;
        s+=t[i]-sum(t[i])+dis[t[i]]-1;
        ans+=(1+s)*s/2;
    }
    printf("%lld\n",ans);
    return 0;
}