[HDU - 3015] Disharmony Trees (树状数组+离散化)

链接

http://acm.hdu.edu.cn/showproblem.php?pid=3015

题意

给你 n n n棵树,每棵树有两个属性 x x x坐标,及 h h h高度,现在定义 F = ∣ x i   −   x j ∣ F= |x_i\ -\ x_j| F=xi  xj,其中 x i x_i xi x j x_j xj均是 x x x的排名,如5、8、7、7,排名为1、4、2、2,定义 S = min ⁡ ( h i ,   h j ) S=\min(h_i,\ h_j) S=min(hi, hj),其中 h i h_i hi h j h_j hj也是排名;现在需要你求出 ∑ i , j F ∗ S \displaystyle \sum_{i,j}F*S i,jFS,即所有任意两棵树的 F ∗ S F*S FS的和;

分析

  这里考虑使用树状数组,首先, S S S是一个求最小值的操作,我们考虑将所有树按 h h h值从大到小排序,这样,当我们遍历到第 i i i棵树时,那么 h i h_i hi就是当前的 S S S,即第 i i i棵树与 i i i之前的任意一棵树的 S S S;
  
  之后 S S S相同,我们考虑把 i i i i i i之前的 i   −   1 i\ -\ 1 i  1棵树的 F F F的和求出来,关键就是怎么求 F F F了,对于第 i i i棵树来说,它与之前所有树的 F = ∣ x i   −   x 1 ∣ + ∣ x i   −   x 2 ∣ + ⋅ ⋅ ⋅ + ∣ x i   −   x i − 1 ∣ F= |x_i\ -\ x_1|+|x_i\ -\ x_2|+···+|x_i\ -\ x_{i-1}| F=xi  x1+xi  x2++xi  xi1;
  
  在第 i i i棵树之前的树有两类,一类的排名 x > x i x>x_i x>xi,一类 x ≤ x i x\leq x_i xxi;之后我们使用两个树状数组,分别保存个数与和,即一个树状数组用来求 [ 1 , i − 1 ] [1,i-1] [1,i1]中, x x x小于 x i x_i xi的个数(至于大于 x i x_i xi的个数,用 i i i直接减即可),另一个树状数组用来求 x x x小于 x i x_i xi x x x的和(至于大于 x i x_i xi x x x的和,直接拿当前总和减即可),我们就可以把两类给分开来了,分别求和即可,最后还要乘以一个当前树的 h i h_i hi,最后 i i i枚举到第 n n n棵树累加结果就可以了;
  
  存结果的变量注意开 l o n g   l o n g long\ long long long,不然会爆;

代码
#include <functional>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>

#define INF 0x7f7f7f7f
#define MAXN 100005
#define N 200005
#define P 2
#define MOD 99991

typedef long long ll;

namespace fastIO {
	//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
	//char buf[(1 << 22)], *p1 = buf, *p2 = buf;
	inline int read() {
		char c = getchar(); int x = 0, f = 1;
		while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
		while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
		return x * f;
	}
}

using namespace fastIO;
using namespace std;

/*namespace segment_Tree {
	struct Tree {
		int l, r, max;
	} tree[4 * MAXN];

	inline void push_up(int x) {
		tree[x].max = max(tree[x << 1].max, tree[x << 1 | 1].max);
	}

	inline void build(int x, int l, int r) {
		tree[x].l = l, tree[x].r = r;
		if (l == r) {
			tree[x].max = 0;
			return;
		}
		int mid = (l + r) >> 1;
		build(x << 1, l, mid);
		build(x << 1 | 1, mid + 1, r);
		push_up(x);
	}

	inline void update(int x, int pos, int val) {
		int l = tree[x].l, r = tree[x].r;
		if (l == r) {
			tree[x].max = max(tree[x].max, val);
			return;
		}
		int mid = (l + r) >> 1;
		if (pos <= mid)update(x << 1, pos, val);
		else update(x << 1 | 1, pos, val);
		push_up(x);
	}

	inline int query(int x, int l, int r) {
		int le = tree[x].l, ri = tree[x].r;
		if (l <= le && ri <= r) {
			return tree[x].max;
		}
		int mid = (le + ri) >> 1;
		int maxm = 0;
		if (l <= mid) maxm = max(maxm, query(x << 1, l, r));
		if (r > mid) maxm = max(maxm, query(x << 1 | 1, l, r));
		return maxm;
	}
}*/
struct tree {
	int h, x;
	bool operator<(const tree &a)const {
		return h > a.h;
	}
}t[MAXN];

int n, a[MAXN], b[MAXN], c1[MAXN], c2[MAXN];

void insert(int x, int val, int c[]) {
	for (int i = x; i <= n; i += i & -i)
		c[i] += val;
}

int sum(int x, int c[]) {
	int res = 0;
	for (int i = x; i > 0; i -= i & -i)
		res += c[i];
	return res;
}


int main() {
	while (scanf("%d", &n)!=EOF) {
		memset(c1, 0, sizeof(c1));
		memset(c2, 0, sizeof(c2));
		for (int i = 1; i <= n; i++)
			a[i] = t[i].x = read(), b[i] = t[i].h = read();
		sort(a + 1, a + 1 + n);
		sort(b + 1, b + 1 + n);
		sort(t + 1, t + 1 + n);
		//这里不需要unique去重
		ll res = 0;
		for (int i = 1; i <= n; i++) {
			int pos1 = lower_bound(a + 1, a + 1 + n, t[i].x) - a;
			int rk = lower_bound(b + 1, b + 1 + n, t[i].h) - b;
			//cout << rk << endl;
			int sum1 = sum(pos1, c1);
			int sum2 = sum(pos1, c2);
			res += 1ll * rk * (1ll * sum1 * pos1 - sum2 + (sum(n, c2) - sum2) - 1ll * (i - sum1 - 1) * pos1);
			insert(pos1, 1, c1);
			insert(pos1, pos1, c2);
			//cout << sum1 <<" " << sum2 <<" " <<res << endl;
		}
		cout << res << endl;
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值