树状数组 公式推导 poj 1990 MooFest

来源:互联网 发布:在excel中做数据透视表 编辑:程序博客网 时间:2024/05/07 23:56

点击打开poj 1990

MooFest
Time Limit: 1000MS Memory Limit: 30000KTotal Submissions: 5918 Accepted: 2591

Description

Every year, Farmer John's N (1 <= N <= 20,000) cows attend "MooFest",a social gathering of cows from around the world. MooFest involves a variety of events including haybale stacking, fence jumping, pin the tail on the farmer, and of course, mooing. When the cows all stand in line for a particular event, they moo so loudly that the roar is practically deafening. After participating in this event year after year, some of the cows have in fact lost a bit of their hearing. 

Each cow i has an associated "hearing" threshold v(i) (in the range 1..20,000). If a cow moos to cow i, she must use a volume of at least v(i) times the distance between the two cows in order to be heard by cow i. If two cows i and j wish to converse, they must speak at a volume level equal to the distance between them times max(v(i),v(j)). 

Suppose each of the N cows is standing in a straight line (each cow at some unique x coordinate in the range 1..20,000), and every pair of cows is carrying on a conversation using the smallest possible volume. 

Compute the sum of all the volumes produced by all N(N-1)/2 pairs of mooing cows. 

Input

* Line 1: A single integer, N 

* Lines 2..N+1: Two integers: the volume threshold and x coordinate for a cow. Line 2 represents the first cow; line 3 represents the second cow; and so on. No two cows will stand at the same location. 

Output

* Line 1: A single line with a single integer that is the sum of all the volumes of the conversing cows. 

Sample Input

43 12 52 64 3

Sample Output

57

Source

USACO 2004 U S Open

[Submit]   [Go Back]   [Status]   [Discuss]


思路: 树状数组

分析:

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;typedef long long int64;const int MAXN = 20010;struct Node{    int x;    int v;    bool operator<(const Node& s)const{        return v < s.v;     }};Node node[MAXN];int n;int treeCount[MAXN];int treeDis[MAXN];int lowbit(int x){    return x&(-x);}int64 getSum(int x , int *arr){    int64 sum = 0;    while(x){         sum += arr[x];         x -= lowbit(x);    }    return sum;}void add(int x , int val , int *arr){    while(x < MAXN){         arr[x] += val;          x += lowbit(x);     }}int64 solve(){    int64 ans = 0;    int64 totalDis = 0;    memset(treeCount , 0 , sizeof(treeCount));    memset(treeDis , 0 , sizeof(treeDis));    sort(node , node+n);    for(int i = 0 ; i < n ; i++){        int64 count = getSum(node[i].x , treeCount);         int64 dis = getSum(node[i].x , treeDis);        // left        ans += node[i].v*(count*node[i].x-dis);        // right        ans += node[i].v*((totalDis-dis-(i-count)*node[i].x));        // update         totalDis += node[i].x;        add(node[i].x , 1 , treeCount);        add(node[i].x , node[i].x , treeDis);    }    return ans;}int main(){    while(scanf("%d" , &n) != EOF){         for(int i = 0 ; i < n ; i++)              scanf("%d%d" , &node[i].v , &node[i].x);         printf("%lld\n" , solve());    }    return 0;}


0 0
原创粉丝点击