树状数组-蓝桥杯省赛C++B组-小朋友排队

寵の児 提交于 2020-02-27 23:53:32

树状数组-蓝桥杯省赛C++B组-小朋友排队

题目:

n 个小朋友站成一排。

现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。

每个小朋友都有一个不高兴的程度。

开始的时候,所有小朋友的不高兴程度都是 0。

如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1,如果第二次要求他交换,则他的不高兴程度增加 2(即不高兴程度为 3),依次类推。当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k。

请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。

如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。

输入格式
输入的第一行包含一个整数 n,表示小朋友的个数。

第二行包含 n 个整数 H1,H2,…,Hn,分别表示每个小朋友的身高。

输出格式
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。

数据范围
1≤n≤100000,
0≤Hi≤1000000
输入样例:
3
3 2 1
输出样例:
9
样例解释
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。

题解:

nh[i]ih[i][i+1,n]h[i]sum[i]过程类似于冒泡排序,对于n个无序数,通过相邻两项交换得到有序序列,每一个数h[i]需要交换的次数取决于\\前i个数当中比h[i]大的数的个数与区间[i+1,n]的数中比h[i]小的数的个数之和,记为sum[i]。

30+2=221+1=212+0=2如样例,3需要交换的次数为0+2=2次,2需要交换的次数为1+1=2次,1需要交换的次数为2+0=2次。

sum[i]采用树状数组求sum[i]的过程:

h[i]h[i]①、从前往后遍历,求和加上高度大于h[i]的数的总个数,接着将h[i]加入到树状数组中去。

h[i]h[i]②、从后往前遍历,求和加上高度小于h[i]的数的总个数,接着将h[i]加入到树状数组中去。

注意,在②开始之前,树状数组需要先清空①操作的数据。

i"W[i]=j=1sum[i]j=(sum[i]+1)×sum[i]2那么对第i个数,其“不高兴程度"W[i]=\sum_{j=1}^{sum[i]}j=\frac{(sum[i]+1)×sum[i]}{2}。

W[i]最终结果就是对W[i]求和。

n105W[i]5×109intW[i]llll(ll)((sum[i]+1)×sum[i]2),int(ll)(sum[i]+1)×sum[i]2注意:n的范围到10^5,W[i]的极端情况可能达到约5×10^9爆了int,最终对W[i]求和的结果用ll来存。\\并且,要先转化ll再相乘,否则在计算过程中会爆,如(ll)(\frac{(sum[i]+1)×sum[i]}{2})是先计算再转化,int是会溢出的。\\正确写法是(ll)\frac{(sum[i]+1)×sum[i]}{2}。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define inf 0x7fffffff
using namespace std;
const int N=1e6+10;
int n,h[N],tr[N];
int sum[N];///sum[i]:前i个中高度大于h[i]和i后面高度小于h[i]的数量之和

int lowbit(int x)
{
    return x&(-x);
}

void add(int p,int w)
{
    for(int i=p;i<=N;i+=lowbit(i)) tr[i]+=w;
}

int query(int x)
{
    int res=0;
    for(int i=x;i>0;i-=lowbit(i)) res+=tr[i];
    return res;
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d",&h[i]),h[i]++;///下标从1开始

    ///从前往后遍历,求每个数前面有多少个数比它大
    for(int i=1;i<=n;i++)
    {
        sum[i]+=query(N)-query(h[i]);
        add(h[i],1);///当前这个数算进去
    }

    ///从后往前遍历,求每个数后面有多少个数比它小
    memset(tr,0,sizeof(tr));///置0!!!不能忘
    for(int i=n;i>0;i--)
    {
        sum[i]+=query(h[i]-1);
        add(h[i],1);
    }

    ll ans=0;
    for(int i=1;i<=n;i++) ans+=(ll)sum[i]*(sum[i]+1)/2;

    cout<<ans<<endl;

    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!