地址:http://poj.org/problem?id=1990
题意:
给出n只,每只两个属性:
v,x
每两只的和为:sum=max(v1,v2)*fabs(x1-x2),求所有n*(n-1)/2对的和
解析:
对v值进行从小到大排序,那么对于当前羊的v值,它之前的羊都要乘这个v。
那么对于当前羊,需要求它之前的sum值。v这个问题已经解决,那么x呢?
它之前的羊的x,要么比它大,要么比它小,所以对于这个fabs的求法是不同的,需要解决这个问题。
引入两个树状数组,num[i]:比i值小的x的个数 ds[i]:比i小的x的和。
对于当前羊 i :
ll sx=getsum(st[i].x,num);
ll sm=getsum(st[i].x,ds);
求出了比羊 i 的 x 的小的x的个数,以及它们的和
所以对sum的贡献就是:
ll small=sx*st[i].x-sm;//因为当前羊的x,要被减sx次,所以直接乘sx
定义tot为羊 i 之前的x总和,那么已知比此羊小的x的和sm,那么自然就求出了比它大的和:
tot-sm
那么有:i-1-sx个st[i].x被减掉。总的来说就是:
ll big=tot-sm-(i-1-sx)*st[i].x;
剩下的就不解释了,看代码吧:
#include <cstdio> #include<cstring> #include<map> #include<algorithm> #include<vector> #include<iostream> using namespace std; typedef long long ll; const int maxn = 2e4+100; ll num[maxn],ds[maxn]; int n; struct node { int v,x; }st[maxn]; bool cmp(node a,node b){ return a.v<b.v; } ll lowbit(ll x) { return x&(-x); } ll getsum(int id,ll c[]) { ll an=0; for(int i=id;i>0;i-=lowbit(i)) an+=c[i]; return an; } void update(ll c[],int id,ll x) { for(int i=id;i<maxn;i+=lowbit(i)) c[i]+=x; return ; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&st[i].v,&st[i].x); ll all=0; ll tot=0; sort(st+1,st+1+n,cmp); for(int i=1;i<=n;i++) { ll sx=getsum(st[i].x,num); ll sm=getsum(st[i].x,ds); ll small=sx*st[i].x-sm; ll big=tot-sm-(i-1-sx)*st[i].x; all=all+(small+big)*st[i].v; //记录答案 tot+=st[i].x; //更新i之前的和值 update(ds,st[i].x,st[i].x);//更新比它x小和 update(num,st[i].x,1);//更新比它x小的数目 } cout<<all<<endl; }