权值线段树

先给出权值线段树的概念:

对一个数组 a a a 构造一个数组 b b b,其 b j b_j bj 表示 a a a i i i 出现的次数,用 b b b 建立的线段树就是权值线段树

不那么通俗的理解:

在普通的线段树中一个节点通常对应一段区间,而在权值线段树中一个节点通常对应一段值域

例题1(洛谷P1637):

在含有 n n n 个整数的序列 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an 中,三个数被称作 thair 当且仅当 i < j < k i<j<k i<j<k a i < a j < a k a_i<a_j<a_k ai<aj<ak
求一个序列中 thair 的个数。

很容易发现我们可以枚举 j j j,算出可行的 i i i 的个数,再算出可行的 k k k 的个数,根据乘法原理 i × k i \times k i×k 就是以 j j j 为中点的 thair 个数(原题数据很水 O ( n 2 ) O(n^2) O(n2) 暴力能过QAQ)。

而根据权值线段树,比 j j j 小的就是 1 1 1 a i − 1 a_i-1 ai1 的数的个数,比 j j j 大的就是 a i + 1 a_i+1 ai+1 1 0 5 10^5 105 的数的个数,而更新 a i a_i ai 个数只需要 a i a_i ai a i a_i ai 1 1 1 即可,但 k k k 还要求下标必须大于 j j j 的下标,所以只需要再倒着跑一遍线段树即可。

代码:

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 2 * 1e5 + 5;
int n, m, a[N], tree[N << 2], les[N], great[N], cnt[N];

void push_up(int cur){
	tree[cur] = tree[cur * 2] + tree[cur * 2 + 1];
}
void build(int cur, int l, int r){
	if(l == r){
		tree[cur] = 0;
		return ;
	}
	int mid = (l + r) / 2;
	build(cur * 2, l, mid);
	build(cur * 2 + 1, mid + 1, r);
	push_up(cur);
}
void update(int cur, int l, int r, int qx, int qy, int val){
	if(qx > r || qy < l){
		return ;
	}if(qx <= l && r <= qy){
		tree[cur] ++;
		return ;
	}
	int mid = (l + r) / 2;
	update(cur * 2, l, mid, qx, qy, val);
	update(cur * 2 + 1, mid + 1, r, qx, qy, val);
	push_up(cur);
}
int query(int cur, int l, int r, int qx, int qy){
	if(qx > r || qy < l){
		return 0;
	}if(qx <= l && r <= qy){
		return tree[cur];
	}
	int mid = (l + r) / 2;
	return query(cur * 2, l, mid, qx, qy) + query(cur * 2 + 1, mid + 1, r, qx, qy);
}
//上面全是线段树模板QAQ
signed main(){
	cin >> n;
	for(int i = 1; i <= n; i ++){
		cin >> a[i];
	}
	build(1, 1, 1e5);//第一遍线段树
	for(int i = 1; i <= n; i ++){//由于之前加进去的数肯定比 a[i] 下标小,所以正序即可
		les[i] = query(1, 1, 1e5, 1, a[i] - 1);//统计比 a[i] 小的数的个数
		update(1, 1, 1e5, a[i], a[i], 1);//将 a[i] 计入个数
	}
	build(1, 1, 1e5);//第二遍线段树
	int ans = 0;
	for(int i = n; i >= 1; i --){
		great[i] = query(1, 1, 1e5, a[i] + 1, 1e5);//统计比 a[i] 大的数的个数
		update(1, 1, 1e5, a[i], a[i], 1);//还是将 a[i] 计入个数
		ans += les[i] * great[i];//统计答案
	}
	cout << ans;
	return 0;
} 

完结撒花!!!

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值