poj 1990 树状数组

传送门:https://vjudge.net/problem/POJ-1990

题意:m头牛,每头牛有两个值v和x。然后每两头牛之间的值是abs(x1-x2) * Max(v1,v2)。问所有m*(m-1)/2对牛之间值的总和。

白书上来的。就是用树状数组做。首先肯定是按照v排序,这样就可以不用管v了。接下来我们看看x。

我们先对所有牛的x排序,然后每头牛有一个idx代表这头牛的x在所有牛的x中排第几位。然后有两个树状数组,num[N]和dis[N]。num[i]表示比i小的有多少个,dis[i]表示比i小的x的和(这里的i指在X[N]数组排名第i位)。然后我按照v从小到大的顺序枚举每一头牛,每次进行维护,这样枚举到的牛所查询树状数组里的都是v比它小的。这样这头牛对答案的贡献就是由两部分构成。

第一部分x比它小的 就是比v×(x-xi)的和,整理就是v×(cnt×x-dis[i]),

然后我在记录目前为止总共加了多少个点进树状数组,以及这些点的x的总和totdis。

这样第二部分就是x比它大的就很好求了。

讲的可能不是很清楚,具体看代码吧。

 1 // Cease to struggle and you cease to live
 2 #include <iostream>
 3 #include <cmath>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <algorithm>
 7 #include <queue>
 8 #include <vector>
 9 #include <set>
10 #include <map>
11 #include <stack>
12 using namespace std;
13 typedef long long ll;
14 const int N=2e4+9;
15 int n;
16 ll num[N<<2],dis[N<<2],X[N];
17 void add(int x){
18     ll val=X[x];
19     for(;x<=n;x+=x&-x) dis[x]+=val,++num[x];
20 }
21 ll qnum(int x){
22     ll ans=0;
23     for(;x>0;x-=x&-x) ans+=num[x];
24     return ans;
25 }
26 ll qdis(int x){
27     ll ans=0;
28     for(;x>0;x-=x&-x) ans+=dis[x];
29     return ans;
30 }
31 struct cow{
32     ll v,x;
33     int idx;
34     bool operator<(const cow& b) const{
35         return v<b.v;
36     }
37 }a[N];
38 int main() {
39     scanf("%d",&n);
40     for(int i=1;i<=n;++i){
41         scanf("%lld%lld",&a[i].v,&a[i].x);
42         X[i]=a[i].x;
43     }
44     sort(X+1,X+1+n);
45     sort(a+1,a+1+n);
46     for(int i=1;i<=n;++i){
47         a[i].idx=lower_bound(X+1,X+1+n,a[i].x)-X;
48     }
49     ll totcnt=0,totdis=0;
50     ll res=0;
51     for(int i=1;i<=n;++i){
52         ll lcnt=qnum(a[i].idx);
53         ll ldis=qdis(a[i].idx);
54         res+=a[i].v*(lcnt*a[i].x-ldis);
55         res+=a[i].v*((totdis-ldis)-(totcnt-lcnt)*a[i].x);
56         ++totcnt;
57         totdis+=a[i].x;
58         add(a[i].idx);
59     }
60     printf("%lld",res);
61     return 0;
62 }
View Code

 

转载于:https://www.cnblogs.com/xiaobuxie/p/10859430.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值