题目链接:POJ 1990
题意:有n头牛,在一条直线上,处于不同的坐标x,不同的听力值v(耳聋值),当i,j想要通话时,需要max(v(i),v(j))*(dist[i]-dist[j])的volume,问这n*(n-1)/2对牛总共的volume时多少。
题解:
首先是音量我们对这些牛按v值降序排列这样,每次当前牛和其前面的牛计算max(v(i),v(j))就等于当前牛的v值。
然后,计算每对的距离之和显然太耗时了,但是题目中给出的是坐标,我们可以将距离之和转化为坐标之和。
那么对于任意的一点i,i之前的牛的音量值肯定小于v[i],但是坐标的值可能比x[i]大也可能比x[i]小,因此我们应该分成两部分来考虑,就是小于xi的和大于xi的,也就是处在这头牛左边的牛和右边的牛。
首先考虑左边的情况,假设左边比小于等于v[i]的牛有三头坐标分别为a b c,那么左边的值就是v[i]*(x[i]-a)+v[i]*(x[i]-b)+v[i]*(x[i]-c) => v[i]*(3*x[i]-(a+b+c))。
那么假设左边有 l 头牛,坐标之和为sum,那么这头牛与左边牛的音量之和就是v[i]*(l*x[i]-sum);
这样就可以将距离转化为坐标之和,不用计算每对i,j的距离。只需要树状数组维护坐标之和即可。在用一个树状数组记录牛的个数。
右边同理。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int MAX=20000+5;
typedef long long LL;
int coor[MAX],cnt[MAX];
int N=20000;
struct node
{
int v,x;
node(int _v = 0 , int _x = 0):v(_v),x(_x){};
bool operator <(const node &a) const
{
return v<a.v;
}
};
node a[MAX];
LL sum(int *bit,int x)
{
LL s=0;
while(x>0)
{
s=s+bit[x];
x-=(x&(-x));
}
return s;
}
void add(int *bit,int x,int v)
{
while(x<=N)
{
bit[x]+=v;
x+=(x&(-x));
}
}
int main()
{
int n;
while(scanf("%d",&n)==1)
{
memset(cnt,0,sizeof(cnt));
memset(coor,0,sizeof(coor));
for(int i=0;i<n;i++)
{
scanf(" %d %d",&a[i].v,&a[i].x);
}
sort(a,a+n);
LL ans=0;
for(int i=0;i<n;i++)
{
ans=ans+1ll*a[i].v*(a[i].x*sum(cnt,a[i].x-1)-sum(coor,a[i].x-1));
ans=ans+1ll*a[i].v*((sum(coor,N)-sum(coor,a[i].x))-a[i].x*(sum(cnt,N)-sum(cnt,a[i].x)));
add(cnt,a[i].x,1);
add(coor,a[i].x,a[i].x);
}
printf("%lld\n",ans);
}
return 0;
}