HDU2838 Cow Sorting 树状数组

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2838


题目大意:Sherlock有N(1≤N≤100,000)头牛一字排开,在晚上挤奶。每头奶牛都有一个唯一的“坏脾气”的值,它们的坏脾气的范围为1... 100,000。由于脾气暴躁的奶牛更容易损坏Sherlock的挤奶设备,福尔摩斯想给奶牛重新排序,使它们按坏脾气增加的顺序排列。在此过程中,任何两个奶牛的地方(相邻)可以互换。由于脾气暴躁的牛难以移动的,它需要福尔摩斯花费的时间为两头奶牛的坏脾气的总和,例如移动坏脾气为X,Y的两头奶牛,需要时间为X+Y。
请帮福尔摩斯计算出重新排序奶牛需要的最短的时间。


分析:该题求一个序列里面所有逆序对的数字和。对于序列里值为x的第i个元素,需要知道前i个元素里比x小的元素的个数以及它们的和,因此需要两个树状数组,一个用来记录比a小的元素的个数cnt,另一个记录它们的和sum,为了简化程序,可以用结构体实现。那么对于移动第i头牛所需时间为sum+cnt×x。



实现代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=100001;
struct node
{
    int cnt;
    long long sum;
}a[M];
int n;
int lowbit(int i)
{
    return i&(-i);
}
void update(int i,int x,int cnt)
{
    while(i<=n)
    {
        a[i].sum+=x;
        a[i].cnt+=cnt;
        i+=lowbit(i);
    }
}
int query_cnt(int n)
{//返回n以前比n小的数的个数
    int ans=0;
    while(n>0)
    {
        ans+=a[n].cnt;
        n-=lowbit(n);
    }
    return ans;
}
long long query_sum(int n)
{//返回n以前多有数的和
    long long ans=0;
    while(n>0)
    {
        ans+=a[n].sum;
        n-=lowbit(n);
    }
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=-1)
    {
        long long ans=0;
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            update(x,x,1);
            long long k=i-query_cnt(x);
            if(k!=0)
            {
                long long tmp=query_sum(n)-query_sum(x);
                ans+=k*x+tmp;
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


你可能感兴趣的:(HDU2838 Cow Sorting 树状数组)