题意:给出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);
}
}