poj 1990 MooFest(树状数组)

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

思路: 树状数组

分析:

1 题目给定n头牛的听力v[i]. 现在规定两头你i和j如果要进行交流的话那么消耗的能量就是dis(i,j)*max(v[i].v[j]),现在问n头牛总共的n*(n-1)*2种方式消耗的总的能量

2 题目要求的是所有的牛的交流方式的总的消耗能量

   看这个样例

   3 1

   2 5

   2 6

   4 3

   那么所有的区间为[1.3],[1,5],[1,6],[3,5],[3,6],[5,6]

   那么总和为4*dis[1.3]+3*dis[1,5]+3*dis[1,6]+4*dis[3,5]+4*dis[3,6]+2*dis[5,6] = 4*(dis[1.3]+dis[3,5]+dis[3,6])+3*(dis[1,5]+dis[1,6])+2*(dis[5,6]);

   那么题目要求的ans = (v[i]*(所有比v[i]小的牛的坐标的总和))


3 那么我们来分解这个式子,我们对点按照音量的值从小到大排完序之后

   那么对于任一的一点i,i之前的牛的音量值肯定小于v[i],但是坐标的值可能比x[i]大也可能比x[i]小,因此我们应该分成两部分来考虑,就是坐标是i的左边和右边


   首先考虑左边的情况,假设左边比小于等于v[i]的牛有三头坐标分别为a b c,那么左边的值就是v[i]*(x[i]-a)+v[i]*(x[i]-b)+v[i]*(x[i]-c) => v[i]*(3*x[i]-(a+b+c))

   那么我们假设左边小于v[i]的牛有countLeft头,总的坐标为totalLeft,那么左边的值为v[i]*(countLeft*x[i]-totalLeft);


   接下来考虑右边的情况,由于我们已经按照v的值排序,那么我们能够很好的计算出小于等于v[i]的音量值的总的坐标之后,我们设为totalDis,那么根据左边求出的小于

   等于v[i]的个数为countLeft,那么右边的个数为i-countLeft,那么同理右边的坐标之和为totalDis-totalLeft , 那么右边的值为v[i]*(totalDis-totalLeft-(i-countLeft)*x[i]);


   那么对于排序后的第i头牛来说比它小的音量的牛的值为v[i]*(countLeft*x[i]-totalLeft)+v[i]*(totalDis-totalLeft-(i-countLeft)*x[i]);


4 我们已经知道了公式,现在我们只要去求countLeft和totalLeft即可,由于我们已经按照v的值排序, 那么我们只要对坐标建立两个树状数组即可。一个用来存储个数,一个用来存储坐标之和,那么对于第i头牛来说我们就能够在O(logn)的时间内求出countLeft和totalLeft


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 100010;

struct Node{
   int x,v;
   bool operator <(const Node& tmp)const
   {
		return v<tmp.v;
   }
}node[MAXN];

int n ;
int treedis[MAXN];
int treeNum[MAXN];

int lowbit(int x){
    return x&(-x);
}

long long  getSum_dis(int x){
    long long  sum = 0;
    while(x){
         sum += treedis[x];
         x -= lowbit(x);
    }
    return sum;
}

long long  getSum_num(int x){
    long long  sum = 0;
    while(x){
         sum += treeNum[x];
         x -= lowbit(x);
    }
    return sum;
}
void add_num(int x , int val){
    while(x < MAXN){
         treeNum[x] += val;
         x += lowbit(x);
    }
}
void add_dis(int x , int val){
    while(x < MAXN){
         treedis[x] += val;
         x += lowbit(x);
    }
}


void  solve(){

	memset(treedis,0,sizeof treedis);
	memset(treeNum,0,sizeof treeNum);
	sort(node,node+n);
	long long totaldis=0,ans=0;
	for(int i=0;i<n;i++)
	{
		long long totalleft=getSum_dis(node[i].x);
		long long countleft=getSum_num(node[i].x);
		ans+=node[i].v*(countleft*node[i].x-totalleft);
		ans+=node[i].v*(totaldis-totalleft-(i-countleft)*node[i].x);
		totaldis+=node[i].x;
		add_dis(node[i].x,node[i].x);
		add_num(node[i].x,1);
	}
	printf("%lld\n",ans);
}

int main(){

    while(~scanf("%d" , &n))
    {
    	for(int i = 0 ; i < n ; i++)
		{
			scanf("%d%d",&node[i].v,&node[i].x);
		}
		solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值