这道题是给出直线上的一些点,每个点都有一个权值,现在要求记录每两个点之间的花费之和是多少,两点之间的花费是指这两个点权值的最大值乘以两点间距离。时间就给了一秒O(n^2)的算法肯定是不行的能优化的地方肯定是求和的部分,所以我们可以用树状数组来把复杂度优化到nlogn,但是当前要求的花费需要知道他的距离和权值才能确定。我们可以把数据都读入后按权值大小排序然后依次把每个点都加入树状数组中,现在只要求出前面所有点到当前点的距离然后再乘以当前点的权值就行了。我们维护两个树状数组,一个求某个点之前所有点的坐标之和,两个求当前点前面有多少点已经加入树状数组了。这样每次计算的时候就可以求出左边的点的个数和他们的坐标和,这部分的花费也就算出来了,右边点的数目可以通过总的点数减去左边点的数目,因为没有重合的点,然后再维护一个之前点的坐标和,右边所有点的坐标和也可以通过相减的方式求出来,然后计算并累加就可以了。这道题开始没思路,看了题解才明白,明白后就自己去写了一下,写完交上去发现我的排名竟然是第一,人品确实不错……
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 20005
#define lowbit(x) (x&(-x))
struct Node
{
int value,pos;
}cow[MAXN];
bool operator < (Node a,Node b)
{
return a.value<b.value;
}
int dis[MAXN],num[MAXN];
void modify(int a[],int i,int v)
{
while(i<=20000)
{
a[i]+=v;
i+=lowbit(i);
}
}
int query(int a[],int i)
{
int ans=0;
while(i>0)
{
ans+=a[i];
i-=lowbit(i);
}
return ans;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d%d",&cow[i].value,&cow[i].pos);
}
sort(cow,cow+n);
long long ans=0,tot=0;
for(int i=0;i<n;i++)
{
int count=query(num,cow[i].pos);
int dist=query(dis,cow[i].pos);
ans+=cow[i].value*(cow[i].pos*count-dist+tot-dist-(i-count)*cow[i].pos);
modify(num,cow[i].pos,1);
modify(dis,cow[i].pos,cow[i].pos);
tot+=cow[i].pos;
}
cout<<ans<<endl;
}