【TOJ 3401.】Disharmony Trees【树状数组】

题意:给出n个树的两个变量,一个是x,一个是h。x的rank称为D,h的rank称为H。对于任意两棵树i, j计算一个F×S, F = abs(Di-Dj), S = min(Hi, Hj)。计算∑F×S


思路:我们对于这n个树对h进行从小到大排序,可以知道对于树i, j, S = Hi(i 在 j前面)。现在的问题是如何求出F,我们可以通过树状数组来计算在i后面的树中Dj有多少个比Di小,有多少个比Di大,并且可以计算出比Di小的Dj的和, 以及比Di大的Dj的和。

           假设有k个比Di小,g个比Di大,并且比Di小的∑D记为Sk, 比Di大的∑D记为Sg,则对于i树所贡献的答案就是Hi×(k×Di-Sk+Sg-g×Di)。现在我们来看如何用树状数组来求出这些数。这里我们用两个树状数组来统计,分别是sum,cnt,我们先提前对于x进行排序,就可以得到D数组,然后从头遍历,遇到Di就在Di的位置上加上Di,(sum), 遇到Di就在Di的位置上加1(cnt)。然后预处理过程就完成了。随后只要在循环中,每遇到一个数,先对于树状数组进行减的操作,因为不包括自己,然后就是区间求值,具体细节见代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define N 100003

struct R{
    int x, h;
    bool operator<(R a)const {
        if (h == a.h) return x < a.x;
        return h < a.h;
    }
}tr[N];

ll sum[N], cnt[N];
int H[N], X[N];

int lb(int k) {return k&-k;}

ll S(int k, int d) {
    ll re = 0;
    while (k > 0) {
        re += d?sum[k]:cnt[k];
        k -= lb(k);
    }
    return re;
}

void add(int k, ll v, int d) {
    while (k < N) {
        if (d) sum[k] += v;
        else cnt[k] += v;
        k += lb(k);
    }
}

ll f(ll a) {return a<0?-a:a;}

int main() {
    int n, i, j, k;
    while (~scanf("%d", &n)) {
        for (i = 1;i <= n;i++) {
            scanf("%d%d", &tr[i].x, &tr[i].h);
            H[i] = tr[i].h, X[i] = tr[i].x;
        }
        sort(tr+1, tr+1+n);
        sort(H+1, H+n+1), sort(X+1, X+n+1);
        memset(sum, 0, sizeof(sum));
        memset(cnt, 0, sizeof(cnt));
        ll ans = 0;
        for (i = 1;i <= n;i++) {
            j = lower_bound(X+1, X+n+1, tr[i].x)-X;
            add(j, j*1ll, 1), add(j, 1, 0);
        }
        for (i = 1;i < n;i++) {
            j = lower_bound(H+1, H+n+1, tr[i].h)-H;
            k = lower_bound(X+1, X+1+n, tr[i].x)-X;
            add(k, -1*1ll, 0), add(k, -k*1ll, 1);
            ll tm = S(k, 0)*k-S(k, 1);
            tm += (S(n, 1)-S(k, 1))-k*(S(n, 0)-S(k, 0));
            ans += j*tm;
        }
        printf("%lld\n", ans);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值