poj1990

题目意思就是有m头牛,每头牛有两个值,v和x,两两之间有一个值,设v分别为v1,v2,x为x1,x2,则它们之间的值为abs(x1-x2) * Max(v1,v2),求所有m*(m-1)/2对牛之间值的总和。
暴力的话很好理解了,但是这里的m数据范围最大为20000,n^2的效率是不可取的。所以这里我们引入树状数组。这道题目我们得用两个两个树状数组,一个叫做num[x],可以计算小于坐标x的坐标有几个,另外一个叫做sum[x],可以将计算小于坐标x的坐标的总和。除此之外,我们还用一个变量total,来保存前面计算机过的坐标总和。
计算方法是这样的,我们拿例子来说,
4
3 1
2 5
2 6
4 3
首先,我们得将它们按照v从小到大排序,这样子每到一头牛,那么只要计算它与之前的坐标差,然后乘上它的v即可。排序后得到:
2 2 3 4
5 6 1 3
我们先计算5这个,发现它的前面没有坐标,那么就没有好计算的,total+=5,更新num和sum。[total=5]
然后计算6,发现它前面有个坐标5,它们之间的坐标差为6*1-5=1,后面暂时没有坐标,ans = ans+1*2,total+=6,更新num和sum。[total=11]
接着计算1,发现它前面没有坐标,但是此时i=3,说明它的后面有3-0-1个坐标,坐标差为 total-1*2 = 9, ans = ans + 9*3,total += 1,更新num和sum。[total=12].
最后计算3,发现它前面有个坐标1,它们之间坐标差为3*1-1=2,后面有4-1-1=2个坐标,坐标差为 total-1-2*3 = 11-6=5,所以坐标差总共为2+5=7,
ans = ans + 7*4 = ans+28,total += 3,更新num和sum。[total = 15]
最后的ans得出就是57。
更具体的详见代码:

#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<time.h>
#include<queue>
#include<stack>
#include<iterator>
#include<math.h>
#include<stdlib.h>
#include<limits.h>
#include<map>
#include<set>
#include<bitset>
//#define ONLINE_JUDGE
#define eps 1e-8
#define INF 0x7fffffff
#define FOR(i,a) for((i)=0;i<(a);(i)++)
#define MEM(a) (memset((a),0,sizeof(a)))
#define sfs(a) scanf("%s",a)
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define pfs(a) printf("%s\n",a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,num) scanf("%d%d%d",&a,&b,&num)
#define for1(i,a,b) for(int i=(a);i<b;i++)
#define for2(i,a,b) for(int i=(a);i<=b;i++)
#define for3(i,a,b)for(int i=(b);i>=a;i--)
#define MEM1(a) memset(a,0,sizeof(a))
#define MEM2(a) memset(a,-1,sizeof(a))
#define ll long long
const double PI=acos(-1.0);
template<class T> T gcd(T a,T b){return b?gcd(b,a%b):a;}
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> inline T Min(T a,T b){return a<b?a:b;}
template<class T> inline T Max(T a,T b){return a>b?a:b;}
using namespace std;
#define M 20010
ll num[M];      //num[x]用来存储在[1,x]之间存储的坐标的个数
ll sum[M];      //sum[x]用来存储在[1,x]之间存储的坐标的和
int n;
int lowbit(int x){
    return x&(-x);
}
struct Cow{
    int val,x;
    bool operator <(const Cow &a) const{
        return val<a.val;
    }
}a[M];
ll getSum(ll *arr,int i){               //树状数组向下求和
    ll sum=0;
    while(i>0){
        sum += arr[i];
        i -= lowbit(i);
    }
    return sum;
}
void upDate(ll *arr,int i,int val){     //树状数组向上更新
    while(i<=n){
        arr[i] += (ll)val;
        i += lowbit(i);
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
#endif
    int m;
    while(sf(m)!=EOF){
        memset(num,0,sizeof num);
        memset(sum,0,sizeof sum);
        n = -1;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&a[i].val,&a[i].x);
            n = Max(n,a[i].x);          //n为最大的坐标,也就是树状数组的右边界
        }
        sort(a+1,a+m+1);        //依volume从小到大排序
        ll total=0;     //记录前i-1头牛所有的volume值的和
        ll ans=0;
        for(int i=1;i<=m;i++){
            ll nm = getSum(num,a[i].x); //计算小于a[i].x的坐标的个数
            ll sm = getSum(sum,a[i].x); //计算小于a[i].x的坐标的和
            ll small = nm*a[i].x-sm;    //计算小于a[i].x的坐标与a[i].x之间的坐标差
            ll big = total-sm-(i-1-nm)*a[i].x;  //计算大于a[i].x的坐标与a[i].x之间的坐标差,
                //其中,total-sm是大于a[i].x的vol和,(i-1-nm)是大于a[i].x的坐标的个数
            ll val = (small+big)*a[i].val; //计算所有小于等于a[i].val的坐标差*a[i].val
            //pfI(val);
            ans += val;
            total += a[i].x;    //更新total
            upDate(num,a[i].x,1);   //更新坐标个数
            upDate(sum,a[i].x,a[i].x);  //更新坐标的和
        }
        pfI(ans);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值