原题链接:点击打开链接
题意:给出一组序列,将序列递增排序,每次移动只能将两个相邻的数进行位置交换,每交换一次需要耗费两个位置上的愤怒值的时间,求 按照要求排序后 ,最少需要消耗多少时间。
思路:
每次移动要牵涉到两个愤怒值,将两点分开 逐个计算每点要加的次数。
<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;
}