POJ - 1990 (MooFest)

题意:n 头牛,每头牛有两个属性值 耳聋程度 w 和坐标 x ( x保证互不相同 ),两头牛i,j可以联系的最低代价是 abs(xi-xj)*max(wi,wj),求所有牛相互联系的最低代价总和;

 

分析:按照耳聋程度从小到大排序,那么每加进一头牛,它的耳聋程度是最大的,即:

\sum_{i=1}^{n} \sum_{j=1}^{i-1}abs(x[i]-x[j])*w[i]; 

那么问题就转化成每一次加进一头牛如何求它与之前所有牛的距离和,到这个点,区间维护的问题,应该可以想到用线段树或者树状数组做了

 

树状数组:

#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();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值