MooFest (树状数组)

博客介绍了如何利用树状数组解决一道关于奶牛听力问题的算法题。题目中,n只奶牛在一条直线上,每只奶牛能听到声音的距离不同。要求计算所有奶牛互相可以听到的音量总和。通过将奶牛按听力大小排序,使用树状数组来维护前面奶牛的数量和位置之和,从而高效地计算出答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:http://poj.org/problem?id=1990

题意:n个耳背的奶牛,第i个奶牛听到声音需要v[i]的分贝,站在x[i]位置的直线上,两个奶牛之间对话的音量是max(v[i],v[j])*两者之间的距离,问所有奶牛互相可以听到的音量和。

思路:将所有奶牛按照可听到音量的大小进行排序,两只奶牛对话所需的分贝就是排在后面的奶牛的 v,所以排在后面的奶牛对排在前面的奶牛对话的音量就可以求了。

第 i个奶牛的分贝是 v[i],设所有分贝排在他前面的奶牛有m个,所以与其中站在它前面的奶牛,每对所需音量:

(x-x1)*f, (x-x2)*f,...,x是第i个奶牛的位置,其他是站在前面且排在前面的奶牛位置,所以加起来就是(cnt*x-sum)*f,cnt是排且站在前面的奶牛个数,sum是他们的位置和。 树状数组维护cnt和sum。

剩下站在后面的奶牛是(x1-x)*f,  (x2-x)*f,...,加起来就是(all - sum -(i-1-cnt)*x)*f, all是所有排在前面的奶牛位置和,sum就是上面的sum,all-sum是(x1+x2+...),i就是加入到第i个奶牛,(i-1-cnt)就是排在i前面但位置在后面的奶牛个数。

所以就出来了      ans += (cnt*x-sum+all - sum -(i-1-cnt)*x) * f。

一边算一边加入,最后就会把所有 n*(n-1)/2对奶牛算完。

记得 long long,wa了好几发,还由于把n的输入格式写错,wa了(捂脸。

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 2e4 + 10;
struct cow
{
	ll vol,pos;
	bool operator<(const cow &a)const  //第一次用构造函数,记得,记得,记得return和两个const
	{
		return vol < a.vol;
	}
}cows[maxn];
ll sum[maxn],cont[maxn];

ll lowbit(int x)
{
	return x&(-x);
}
void update(int x,ll d,bool flag)
{
	while(x < maxn)
	{
		if(flag) cont[x]++;
		else sum[x] += d;
		x += lowbit(x);
	}
}
ll query(int x,bool flag)
{
	ll res = 0;
	while(x > 0)
	{
		if(flag) res += cont[x];
		else res += sum[x];
		x -= lowbit(x);
	}
	return res;
}
int main()
{
	ll ans = 0;
	int n;
	scanf("%d",&n);
	for(int i=1; i<=n; ++i)
	{
		scanf("%lld%lld",&cows[i].vol,&cows[i].pos);
	}
	sort(cows+1,cows+n+1);
	for(int i=1; i<=n; ++i)
	{
		ll sum = query(cows[i].pos,0),cnt = query(cows[i].pos,1),all = query(maxn,0);
		ans += cows[i].vol*(cnt * cows[i].pos - sum + all - sum - (i - 1 - cnt) * cows[i].pos);
		updata(cows[i].pos,0,true);
		updata(cows[i].pos,cows[i].pos,false);
	}
	printf("%lld\n",ans);
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值