题意:有一群牛在开聚会,简称吹牛比大会,有坐标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);
}