题意:n 头牛,每头牛有两个属性值 耳聋程度 w 和坐标 x ( x保证互不相同 ),两头牛i,j可以联系的最低代价是 abs(xi-xj)*max(wi,wj),求所有牛相互联系的最低代价总和;
分析:按照耳聋程度从小到大排序,那么每加进一头牛,它的耳聋程度是最大的,即:
那么问题就转化成每一次加进一头牛如何求它与之前所有牛的距离和,到这个点,区间维护的问题,应该可以想到用线段树或者树状数组做了
树状数组:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 20000+10;
ll lowbit(ll x){return x&(-x);}
struct node{
int x,w;
node(){}
node(int _w,int _x){
w=_w,x=_x;
}
bool operator < (const node& a)const{
return w<a.w;
}
}e[N];
ll num[N];
ll sum[N];
int m;
void add(ll s[],ll k,ll x){
while(k<=m){
s[k]+=x;
k+=lowbit(k);
}
}
ll ssum(ll s[],ll k){
ll sum=0;
while(k>0){
sum+=s[k];
k-=lowbit(k);
}
return sum;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&e[i].w,&e[i].x),m=max(m,e[i].x);
sort(e+1,e+n+1);
ll ans=0;
for(int i=1;i<=n;i++){
ll num1=ssum(num,e[i].x); //比当前牛坐标小的已经遍历过的牛的数量
ll num2=ssum(num,m); //前面所有牛的数量
ll sum1=ssum(sum,e[i].x); //比当前牛坐标小的遍历过的牛的坐标和
ll sum2=ssum(sum,m); //前面所有牛的坐标和
ans+=e[i].w*((sum2-sum1)-(num2-num1)*e[i].x+num1*e[i].x-sum1);
add(num,e[i].x,1); //更新
add(sum,e[i].x,e[i].x);
}
printf("%lld",ans);
}
线段树:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 20000+10;
struct node{
int id,x,w;
node(){}
node(int _id,int _x,int _w){
id=_id,x=_x,w=_w;
}
}e[N];
bool cmp1(node a,node b){
return a.x<b.x;
}
bool cmp2(node a,node b){
return a.w<b.w;
}
int n;
void input(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x,w;
scanf("%d%d",&w,&x);
e[i]=node(0,x,w);
}
sort(e+1,e+n+1,cmp1);
for(int i=1;i<=n;i++) e[i].id=i;
sort(e+1,e+n+1,cmp2);
}
struct edge{
ll sum,num;
edge(){}
edge(ll _num,ll _sum){
num=_num,sum=_sum;
}
}tree[N<<2];
edge operator + (edge a,edge b){
return edge(a.num+b.num,a.sum+b.sum);
}
void build(int l,int r,int rt){
if(l==r){
tree[rt]=edge(0,0);
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void update(int l,int r,int rt,int id,int x){
if(l==r){
tree[rt]=edge(1,x);
return;
}
int m=(l+r)>>1;
if(id<=m) update(l,m,rt<<1,id,x);
else update(m+1,r,rt<<1|1,id,x);
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
edge query(int l,int r,int rt,int L,int R){
if(L>R) return edge(0,0);
if(L<=l&&r<=R){
return tree[rt];
}
int m=(l+r)>>1;
edge A=edge(0,0),B=edge(0,0);
if(m<R) A=query(m+1,r,rt<<1|1,L,R);
if(m>=L) B=query(l,m,rt<<1,L,R);
return A+B;
}
void solve(){
build(1,n,1);
ll ans=0;
for(int i=2;i<=n;i++){
update(1,n,1,e[i-1].id,e[i-1].x);
edge des=query(1,n,1,1,e[i].id);
edge as =query(1,n,1,e[i].id,n);
ans+=(ll)e[i].w*(des.num*e[i].x-des.sum);
ans+=(ll)e[i].w*( as.sum-as.num*e[i].x);
}
printf("%lld",ans);
}
int main()
{
input();
solve();
}