题意:FJ有n头牛,排列成一条直线(不会在同一个点),给出每头牛在直线上的坐标x。另外,每头牛还有一个自己的声调v,如果两头牛(i和j)之间想要沟通的话,它们必须用同个音调max(v[i],v[j]),沟通起来消耗的能量为:max(v[i],v[j]) * 它们之间的距离。问要使所有的牛之间都能沟通(两两之间),总共需要消耗多少能量。
思路:树状数组。很好的一道题。把牛按x升序排列,然后:(很难表示清楚,囧...)
1:把沟通分成向左和向右,向左就是:v[右] > v[左],它们之间取右边牛的声调。
2:先求向左的总能量:运用2个树状数组,都以牛的声调做下标,arNum[]用于求某个声调范围内牛的数量,arDis[]用于求某个声调范围内牛的总距离(与起点0的总距离),这对于当前的牛(x,v)来说,用它的音调向左沟通消耗的总能量则为:v * 左边声调比它小的牛和它的距离差之和(sum(v-1, arNum)* sum(v-1, arDis))。
3:相同的道理求出向右的总能量。
代码如下:
#include<iostream>
#include<algorithm>
#define lowbit(x) (x&(-x))
using namespace std;
const int MAX = 20005;
struct data
{
int x, w;
}cow[MAX];
int arNum[MAX], arDis[MAX];
bool cmp(data a, data b)
{
return a.x < b.x;
}
void add(int i, int *ar, int w)
{
while(i <= MAX-1)
{
ar[i] += w;
i += lowbit(i);
}
}
__int64 sum(int i, int *ar)
{
__int64 ans = 0;
while(i > 0)
{
ans += ar[i];
i -= lowbit(i);
}
return ans;
}
int main()
{
int n, i;
__int64 preNum, preDis;
scanf("%d", &n);
for(i = 0; i < n; i ++)
scanf("%d%d", &cow[i].w, &cow[i].x);
sort(cow, cow + n, cmp);
__int64 ans = 0;
memset(arNum, 0, sizeof(arNum)); // 求向左的总能量。
memset(arDis, 0, sizeof(arDis));
for(i = 0; i < n; i ++)
{
preNum = sum(cow[i].w-1, arNum);
preDis = sum(cow[i].w-1, arDis);
ans += (preNum * cow[i].x - preDis) * cow[i].w;
add(cow[i].w, arNum, 1);
add(cow[i].w, arDis, cow[i].x);
}
memset(arNum, 0, sizeof(arNum)); // 求向右的总能量。
memset(arDis, 0, sizeof(arDis));
for(i = n-1; i >= 0; i --)
{
preNum = sum(cow[i].w, arNum); // 这里不要用w-1,考虑了声调相等的情况。
preDis = sum(cow[i].w, arDis);
ans += (preDis - preNum * cow[i].x) * cow[i].w;
add(cow[i].w, arNum, 1);
add(cow[i].w, arDis, cow[i].x);
}
printf("%I64d\n", ans);
return 0;
}