hdu 2838 树状数组

原题链接:点击打开链接

题意:给出一组序列,将序列递增排序,每次移动只能将两个相邻的数进行位置交换,每交换一次需要耗费两个位置上的愤怒值的时间,求 按照要求排序后 ,最少需要消耗多少时间。

思路:

每次移动要牵涉到两个愤怒值,将两点分开 逐个计算每点要加的次数。

<span style="color:#ff0000;">对于每个愤怒值,他本身贡献的次数等于 它左边大于它的数的个数+右边小于它的数的个数</span>

7 9 8 4,

每个点相加的次数,按照冒泡排序的顺序:

9:9的右边比9小的数有两个 9要加两次。   9总共加了2次

8:8的右边比8小的数有1个 要加1次,8的左边比8大的数有1个加一次。8总共加了两次   

7: 7的右边比7小的数有1个 ,7要移动到4的后面,7要加一次 。   7总共加了1次

4:   4的左边比4大的数有3个,要加3次。   4总共加了3次

总共相加了的次数:

4+8 4+9 4+7 8+9

与每点相加的次数相同。

PS:对每个数冒泡之后会形成一个新的序列,但是不能在 这个新的序列中进行判断,这里是对每个点贡献进行计算的,但是每个数进行冒泡 在移动的每个位置是都会牵涉到两个数

code:

#include <stdio.h>
#include <string.h>
#define N 100001
#define lowbit(i) (i) & (-i)
__int64 c[N],a[N];
void update(int pos,int num)
{
    while(pos<=N)
    {
        c[pos]+=num;
        pos+=lowbit(pos);
    }
}
__int64 sum(int pos)
{
    int sum=0;
    while(pos>0)
    {
        sum+=c[pos];
        pos-=lowbit(pos);
    }
    return sum;
}
int main()
{
   // freopen("i.txt","r",stdin);
    __int64 n,i;
    __int64 ans = 0;
        ans=0;
        memset(c,0,sizeof(c));
        for(i = 1; i <= n; ++i)//对于每个愤怒值,他本身贡献的次数等于 它左边大于它的数的个数+右边小于它的数的个数,
        {
            scanf("%I64d", &a[i]);
            __int64 t=sum(a[i]);
            ans += a[i] * (i -1- t);//sum(a[i])==左边比a[i]小的数的个数
            update(a[i],1);
        }
        memset(c,0,sizeof(c));
        __int64 j = 1;
        for(i = n; i>=1; i--,j++)
        {
            __int64 t=sum(a[i]);
            ans += (a[i] * t);// sum(a[i])表示 右边比a[i]小的数的个数
            update(a[i],1);
        }
        printf("%I64d\n",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值