POJ1990 MooFest题解(树状数组)

本文同步发布于个人博客

题意简述

n n n个牛每个牛有听力 v v v和位置 x x x,对于每个牛对 ( i , j ) (i,j) (i,j)能产生 m a x ( v i , v j ) ⋅ ∣ x i − x j ∣ max(v_i,v_j)\cdot |x_i-x_j| max(vi,vj)xixj求所有牛对的能量和

题解

首先应该想到是一个统计题,那 m a x max max就有点难处理,不妨给定一个顺序把牛按听力排序,这样一边遍历一边处理
现在假设已经考虑到 i i i,前面都是听力比 i i i小的,那么距离和如何统计呢?我们可以做一个分类,距离比 i i i小的和距离比 i i i大的
对于距离比 i i i小的,我们求出距离比 i i i小的个数 t t t,这 t t t个牛距离和为 s u m 1 sum_1 sum1,那么对答案就能产生 t ⋅ x i − s u m t\cdot x_i-sum txisum的贡献,把这个贡献设为 a n s 1 ans_1 ans1
对于距离比 i i i大的,我们把目前所有的距离和 s u m sum sum减去小于 x x x的距离和,那么对答案产生的贡献就是 s u m − s u m 1 − ( i − t − 1 ) ⋅ x i sum-sum_1-(i-t-1)\cdot x_i sumsum1(it1)xi,这两个贡献加起来乘以 v i v_i vi即可
然后每次统计完一个的贡献以后,就把当前距离加进考虑范围,由于两个查询都是查询前缀和,并且是单点修改,所以我们可以用两个树状数组来维护,两个下标都是坐标,一个维护比当前坐标小的牛的个数,另一个维护比当前坐标小的牛的距离的和即可

const int maxn=20050;
const int N=20000;
ll n,c1[maxn],c2[maxn];
struct node{
	ll v,x;
	bool operator<(const node&t) const{
		return v<t.v;
	}
}a[maxn];
int lowbit(int x){return x&-x;}
void add1(int x,int p)
{
	while(x<=N)
	{
		c1[x]+=p;
		x+=lowbit(x);
	}
}
void add2(int x,int p)
{
	while(x<=N)
	{
		c2[x]+=p;
		x+=lowbit(x);
	}
}
ll query1(int x)
{
	ll res=0;
	while(x)
	{
		res+=c1[x];
		x-=lowbit(x);
	}
	return res;
}
ll query2(int x)
{
	ll res=0;
	while(x)
	{
		res+=c2[x];
		x-=lowbit(x);
	}
	return res;
}
int main()
{
	cin>>n;
	memset(c1,0,sizeof(c1));
	memset(c2,0,sizeof(c2));
	for(int i=1;i<=n;i++) read(a[i].v),read(a[i].x);
	sort(a+1,a+n+1);
	ll ans=0,sum=0;
	for(int i=1;i<=n;i++)
	{
		ll t=query1(a[i].x);
		ans+=(a[i].x*t-query2(a[i].x))*a[i].v;
		ans+=(sum-query2(a[i].x)-(i-t-1)*a[i].x)*a[i].v;
		sum+=a[i].x;
		add1(a[i].x,1);
		add2(a[i].x,a[i].x);
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值