poj1990~MooFest(哥拿下的第一个树状数组)

题意:有一群牛在开聚会,简称吹牛比大会,有坐标x和听觉参量v,他们两两之间要各交谈一次,每次交谈要花去(两者之间v的最大值)*(他们之间的距离)的时间;问总共要花去多少时间

思路:今天早上看了树状数组,还不会的同学请看:

 http://blog.csdn.net/a799581229/article/details/38119371

然后我来看这题,愣是没看懂题解里给的那个关键公式。午睡醒来后自己推了下,一下子就推出来啦。

我讲解下面的公式时,首先确保你已经会了树状数组,不必搞懂i&(-i)啦,会用sum和update就行。

首先,因为交谈所需的时间中v取的是二者的最大值,所以要对牛们按照v的大小进行排序,从小到大,进行操作,这个应该好理解。

放上题目里给的例子

4

3 1

2 5

2 6

4 3

排序后变成  v   2  2  3  4

          x   5  6  1  3

          i    1   2  3  4

需要两个数组,count[ x]用来计算0到x间插入过多少个数字,total[x]用来计算0到x间插入过的坐标总和。

需要一个变量bigtotal=0,计算已经插入过的坐标总和

什么用下面会讲。

首先,第一头牛,肯定是没有交谈的对象(好可怜),不去讲他的计算过程,因为比他小的v没有了。

所以调用函数update(count,x,1),update(total,x,x),即往0到x的区间数量+1,0到x区间的坐标和+1。

然后bigtotal+=x。

然后是第二头牛,x=6,通过sum(count,x)函数可知在它的坐标前面有过1头牛,而且这头牛的坐标和为5,所以他们之间的坐标差=1*6 - 5=1,然后ans+=1*v=1*2=2。然后再调用update更新区间和bigtotal=11。

然后是第三头牛,x=1.,咦,在他的坐标前面没有牛,但是x=1的后面有x=5和x=6,两头牛,这怎么办?那就计算比x=1大的牛的数量和坐标和。因为i=3,说明现在总共有3头牛,然而通过sum函数查询count得知=0,说明0到1之间没有牛,那么说明在它的身后应该有3-1-0=2头牛,这两头牛的坐标和等于(总坐标和减去0到1之间的坐标和)=11。然后坐标差=11-2*1=9,所以ans+=9.

后面第四头牛x=3既有比x小的牛也有比x大的牛,把二、三两头牛的计算方法结合起来。

至此,得出主要代码:

	 for(i=1;i<=m;i++)
	 {
		j=cow[i].x;     //j就是坐标
		c=sum(amount,j);  //查询0到j之间的坐标个数
		t=sum(total,j);   //查询0到j之间的坐标总和
		less=c*j-t;      //在比j小的坐标的坐标差
		more=bigtotal-t-(i-c-1)*j;  //比j大的坐标的坐标差
		ans+=(less+more)*cow[i].v;   //计算ans
		bigtotal+=j;         //总坐标差更新
		update(amount,j,1);  //区间更新
		update(total,j,j);     //区间更新
	 }


 

最后记住用_I64d,因为数字会很大~

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
#define M 20005

struct COW
{
	int v,x;
};
COW cow[M];

int cmp(struct COW &a,struct COW &b) //递增的排序
{
    if(a.v<b.v)
        return 1;
    else
        return 0;
}

int n;
__int64 amount[M];
__int64 total[M];

__int64 sum(__int64 *c,int i)    //查询0到i区间的数值状况
{
	int s=0;
	while(i>0)
	{
		s+=c[i];
		i-=i&(-i);
	}
	return s;
}

void update(__int64 *c,int i,int value)//更新0到i区间的数值状况
{
	while(i<=n)
	{
		c[i]+=value;
		i+=i&(-i);
	}
}

int main()
{
	int y,i,j,m;
	n=0;
	memset(amount,0,sizeof(amount));
	memset(total,0,sizeof(total));
	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&cow[i].v,&cow[i].x);
		if(cow[i].x>n) n=cow[i].x;
	}
	 sort(cow+1,cow+m+1,cmp);  //从i=1开始的,所以要+1
	 int c;
	 __int64 t,less,more,bigtotal=0,ans=0;

	 for(i=1;i<=m;i++)
	 {
		j=cow[i].x;     //j就是坐标
		c=sum(amount,j);  //查询0到j之间的坐标个数
		t=sum(total,j);   //查询0到j之间的坐标总和
		less=c*j-t;      //在比j小的坐标的坐标差
		more=bigtotal-t-(i-c-1)*j;  //比j大的坐标的坐标差
		ans+=(less+more)*cow[i].v;   //计算ans
		bigtotal+=j;         //总坐标差更新
		update(amount,j,1);  //区间更新
		update(total,j,j);     //区间更新
	 }
	 printf("%I64d\n",ans);
}


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ 2182是一道使用树状数组解决的题目,题目要求对给定的n个数进行排序,并且输出每个数在排序后的相对位置。树状数组是一种用来高效处理前缀和问题的数据结构。 根据引用中的描述,我们可以通过遍历数组a,对于每个元素a[i],可以使用二分查找找到a到a[i-1]中小于a[i]的数的个数。这个个数就是它在排序后的相对位置。 代码中的query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。 最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。 参考代码如下: ```C++ #include <iostream> #include <cstdio> using namespace std; int n, a += y; } } int main() { scanf("%d", &n); f = 1; for (int i = 2; i <= n; i++) { scanf("%d", &a[i]); f[i = i & -i; } for (int i = n; i >= 1; i--) { int l = 1, r = n; while (l <= r) { int mid = (l + r) / 2; int k = query(mid - 1); if (a[i > k) { l = mid + 1; } else if (a[i < k) { r = mid - 1; } else { while (b[mid]) { mid++; } ans[i = mid; b[mid = true; add(mid, -1); break; } } } for (int i = 1; i <= n; i++) { printf("%d\n", ans[i]); } return 0; } ``` 这段代码使用了树状数组来完成题目要求的排序功能,其中query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [poj2182Lost Cows——树状数组快速查找](https://blog.csdn.net/aodan5477/article/details/102045839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [poj_2182 线段树/树状数组](https://blog.csdn.net/weixin_34138139/article/details/86389799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值