《算法竞赛进阶指南》0x42 树状数组

这篇具有很好参考价值的文章主要介绍了《算法竞赛进阶指南》0x42 树状数组。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

0x42 树状数组

楼兰图腾

题意:

二维平面给定一些点,询问 v 形和 ∧ 形数目

解析:

对于 ∧ 形: ( i , y ) (i,y) (i,y),考虑左右两侧比该点低的点的个数。树状数组查询 y j < y y_j< y yj<y 的点的个数。因为总共有 y − 1 y-1 y1 个点比当前点低,有 n − y n-y ny 个点比当前点高。

v型同理。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 4e5+10;
const int N = 4E5;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

ll c[maxn];	
int lowbit(int x){
	return x & (-x);
}
void add(int x, int v){
	for(; x <= N; x += lowbit(x))
		c[x] += v;
}
ll query(int x){
	ll res = 0;
	while(x){
		res += c[x];
		x -= lowbit(x);
	}
	return res;
}
ll lsmall[maxn], lbig[maxn], rsmall[maxn], rbig[maxn], y;
int n;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	//BIT tr;
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> y;
		lsmall[i] = query(y-1); 
		rsmall[i] = y-1-lsmall[i];
		lbig[i] = i-1-lsmall[i];
		rbig[i] = n-y-lbig[i];
		add(y, 1);
	}
	ll ans1 = 0, ans2 = 0;
	for(int i = 1; i <= n; i++){
		ans1 += lbig[i] * rbig[i];
		ans2 += lsmall[i] * rsmall[i];
	}
	cout << ans1 << " " << ans2 << endl;
	return 0;
}

一个简单的整数问题

题意:

区间加,单点查询。

解析:

树状数组维护差分数组。

区间加为单点修改,单点查询为查询差分数组的前缀和。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, m, a[maxn];

int ls(int x){return x << 1;}
int rs(int x){return x << 1 | 1;}
struct sgt{
	int lmax, rmax, maxx, sum;
	sgt(){
		lmax = rmax = maxx = -INF;
		sum = 0;
	}
}t[maxn << 2];
void pushup(int k){
	t[k].sum = t[ls(k)].sum + t[rs(k)].sum;
	t[k].lmax = max(t[ls(k)].lmax, t[ls(k)].sum + t[rs(k)].lmax); 
	t[k].rmax = max(t[rs(k)].rmax, t[rs(k)].sum + t[ls(k)].rmax);	
	t[k].maxx = max(t[ls(k)].maxx, t[rs(k)].maxx);
	t[k].maxx = max(t[k].maxx, t[ls(k)].rmax+t[rs(k)].lmax);
}
void pushup(sgt &k, sgt lson, sgt rson){
	k.sum = lson.sum + rson.sum;
	k.lmax = max(lson.lmax, lson.sum + rson.lmax); 
	k.rmax = max(rson.rmax, rson.sum + lson.rmax);	
	k.maxx = max(lson.maxx, rson.maxx);
	k.maxx = max(k.maxx, lson.rmax+rson.lmax);
}
void build(int k, int l, int r){
	if(l == r){
		t[k].lmax = t[k].rmax = t[k].maxx = t[k].sum = a[l];
		return;
	}
	int mid = (l+r) >> 1;
	build(ls(k), l, mid);
	build(rs(k), mid+1, r);
	pushup(k);
}
void modify(int k, int l, int r, int pos, int w){
	if(l == r && l == pos){
		t[k].lmax = t[k].rmax = t[k].maxx = t[k].sum = w;
		return;
	}
	int mid = (l+r) >> 1;
	if(pos <= mid)
		modify(ls(k), l, mid, pos, w);
	else
		modify(rs(k), mid+1, r, pos, w);
	pushup(k);
}
sgt query(int k, int l, int r, int x, int y){
	if(x <= l && y >= r)
		return t[k];
	int mid = (l+r) >> 1;
	sgt lres, rres, res;
	if(x <= mid)
		lres = query(ls(k), l, mid, x, y);
	if(y > mid)
		rres = query(rs(k), mid+1, r, x, y);
	pushup(res, lres, rres);
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	build(1, 1, n);
	int op, x, y;
	while(m--){
		cin >> op >> x >> y;
		if(op == 1){
			if(x > y)
				swap(x, y);
			cout << query(1, 1, n, x, y).maxx << endl;
		}			
		else if(op == 2){
			modify(1, 1, n, x, y);
		}			
	}
	return 0;
}

一个简单的整数问题2

题意:

区间加,区间查询

解析:

区间加,需要用树状数组维护差分数组。区间查询,需要求出原数组的前缀和数组。

设原数组为 a a a,差分数组为 d d d,前缀和数组为 s u m sum sum

s u m n = ∑ i = 1 n a i = ∑ i = 1 n ( ∑ j = 1 i d j ) = ( n + 1 ) ∑ i = 1 n d i − ∑ i = 1 n i ⋅ d i sum_n = \sum\limits_{i=1}\limits^na_i = \sum\limits_{i=1}\limits^n(\sum\limits_{j=1} \limits^id_j)=(n+1)\sum\limits_{i=1}\limits^nd_i-\sum\limits_{i=1}\limits^ni\cdot d_i sumn=i=1nai=i=1n(j=1idj)=(n+1)i=1ndii=1nidi

所以需要两个树状数组,维护 d d d i ⋅ d i i\cdot d_i idi

树状数组代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 4e5+10;
const int N = 4E5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int lowbit(int x){return x & (-x);}
struct BIT{
	ll c[maxn];	
	void add(int x, ll v){
		for(; x <= N; x += lowbit(x))
			c[x] += v;
	}
	void update(int l, int r, int d){
		add(l, d);
		add(r+1, -d);
	}
	ll query(int x){
		ll res = 0;
		while(x){
			res += c[x];
			x -= lowbit(x);
		} 
		return res;
	}
}tr1, tr2;
ll sum(int x){
	return tr1.query(x) * (x+1) - tr2.query(x);
}
ll n, m, a[maxn];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		tr1.update(i, i, a[i]);
		tr2.add(i, i*a[i]); tr2.add(i+1, -(i+1)*a[i]);
	}
	string op;
	ll l, r, d;
	while(m--){
		cin >> op;
		if(op == "C"){
			cin >> l >> r >> d;
			tr1.update(l, r, d);
			tr2.add(l, l*d); tr2.add(r+1, -(r+1)*d);
		}
		else if(op == "Q"){
			cin >> l >> r;
			cout << sum(r)-sum(l-1) << endl;
		}
	}
	return 0;
}

线段树代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int a[maxn], n, m;
int ls(int x){return x << 1;}
int rs(int x){return x << 1 | 1;}
struct sgt{
	ll v, tag;
}t[maxn << 2];
void pushup(int k){
	t[k].v = t[ls(k)].v + t[rs(k)].v;
}
void build(int k, int l, int r){
	t[k].tag = 0;
	if(l == r){
		t[k].v = a[l];
		return;
	}
	int mid = (l+r) >> 1;
	build(ls(k), l, mid);
	build(rs(k), mid+1, r);
	pushup(k);
}
void pushdown(int k, int l, int r){
	if(t[k].tag){
		int mid = (l+r) >> 1;
		t[ls(k)].v += (mid-l+1) * t[k].tag;
		t[ls(k)].tag += t[k].tag;
		t[rs(k)].v += (r-mid) * t[k].tag;
		t[rs(k)].tag += t[k].tag;
		t[k].tag = 0;
	}
}
void modify(int k, int l, int r, int x, int y, ll w){
	if(x <= l && y >= r){
		t[k].tag += w;
		t[k].v += (r-l+1) * w;
		return;
	}
	int mid = (l+r) >> 1;
	pushdown(k, l, r);
	if(x <= mid)
		modify(ls(k), l, mid, x, y, w);
	if(y > mid)
		modify(rs(k), mid+1, r, x, y, w);
	pushup(k);
}
ll query(int k, int l, int r, int x, int y){
	if(x <= l && y >= r)
		return t[k].v;
	pushdown(k, l, r);
	int mid = (l+r) >> 1;
	ll res = 0;
	if(x <= mid)
		res += query(ls(k), l, mid, x, y);
	if(y > mid)
		res += query(rs(k), mid+1, r, x, y);
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	build(1, 1, n);
	string op;
	int l, r, d;
	while(m--){
		cin >> op;
		if(op == "C"){
			cin >> l >> r >> d;
			modify(1, 1, n, l, r, d);
		}
		else if(op == "Q"){
			cin >> l >> r;
			cout << query(1, 1, n, l, r) << endl;
		}
	} 
	return 0;
}

谜一样的牛

题意:

n n n 头牛,身高为1-n的排列。第 i i i 头牛前有 A i A_i Ai 头牛比该牛矮,询问每头牛的身高。

解析:

只有最后一头牛的身高能直接确定,假设 A n = x A_n = x An=x,则 h n = x + 1 h_n = x+1 hn=x+1。倒数第二头牛的身高也能确定了,设 A n − 1 = y A_{n-1} = y An1=y,则 身高为身高可用集合中第 y + 1 y+1 y+1 小的。

a i a_i ai 表示身高 i i i 是否可用, a i = 1 a_i = 1 ai=1 表示可用。 a a a 的前缀和具有单调性,可以二分。

需要支持区间查询和单点修改。所以用树状数组维护 a a a

时间复杂度为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)文章来源地址https://www.toymoban.com/news/detail-410118.html

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 2e5+10;
const int N = 2e5;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

ll c[maxn];
int lowbit(int x){
	return x & (-x);
}
void add(int x, int v){
	for(; x <= N; x += lowbit(x))
		c[x] += v;
}
ll query(int x){
	ll res = 0;
	while(x){
		res += c[x];
		x -= lowbit(x);
	} 
	return res;
}
ll sum(int l, int r){
	return query(r) - query(l-1);
}
int n, m, a[maxn], h[maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for(int i = 2; i <= n; i++)
		cin >> a[i];
	for(int i = 1; i <= n; i++)
		add(i, 1);
	for(int i = n; i >= 1; i--){
		int l = 1, r = n, pos;
		while(l <= r){
			int mid = (l+r) >> 1;
			if(sum(1, mid) >= a[i]+1){
				r = mid-1;
				pos = mid;
			}
			else
				l = mid+1;
		}
		h[i] = pos;
		add(pos, -1);
	}
	for(int i = 1; i <= n; i++)
		cout << h[i] << endl;
	return 0;
}

到了这里,关于《算法竞赛进阶指南》0x42 树状数组的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法】树状数组和线段树

    O ( l o g n ) O(logn) O ( l o g n ) :单点修改、区间查询 与前缀和的区别: 前缀和是离线的,每次动态修改原数组某个元素,都需要重新求一遍前缀和,因此单点修改是 O ( n ) O(n) O ( n ) 的,区间查询则是 O ( 1 ) O(1) O ( 1 ) 树状数组是在线的,单点修改和区间查询都是 O ( l o g n ) O(

    2024年02月19日
    浏览(45)
  • 【算法 & 高级数据结构】树状数组:一种高效的数据结构(一)

    🚀 个人主页 :为梦而生~ 关注我一起学习吧! 💡 专栏 :算法题、 基础算法~赶紧来学算法吧 💡 往期推荐 : 【算法基础 数学】快速幂求逆元(逆元、扩展欧几里得定理、小费马定理) 【算法基础】深搜 树状数组 (Binary Indexed Tree,BIT)是一种数据结构,用于高效地处理

    2024年03月11日
    浏览(64)
  • 【算法 & 高级数据结构】树状数组:一种高效的数据结构(二)

    🚀 个人主页 :为梦而生~ 关注我一起学习吧! 💡 专栏 :算法题、 基础算法、数据结构~赶紧来学算法吧 💡 往期推荐 : 【算法基础 数学】快速幂求逆元(逆元、扩展欧几里得定理、小费马定理) 【算法基础】深搜 数据结构各内部排序算法总结对比及动图演示(插入排序

    2024年03月26日
    浏览(82)
  • 【算法每日一练]-结构优化(保姆级教程 篇4 树状数组,线段树,分块模板篇)

    目录 分块 分块算法步骤: 树状数组 树状数组步骤: 线段树点更新 点更新步骤: 线段树区间更新 区间更新步骤: 不同于倍增和前缀和与差分序列。 前缀和处理不更新的区间和 差分处理离线的区间更新问题 倍增处理离线的区间最值问题 分块,树状数组,线段树: 分块处

    2024年02月04日
    浏览(43)
  • 数组掌握秘籍:Java数组进阶指南

    数组是一种用于存储多个相同类型元素的数据结构,它具有连续的内存空间和相同的数据类型。数组可以在内存中保存多个相同类型的值,并通过索引进行访问和操作。 一维数组是最简单的数组形式,它只包含一个维度。一维数组可以存储多个相同类型的元素。 Java中创建一

    2024年02月15日
    浏览(39)
  • 数组越界在算法竞赛中可能产生的问题

    数组越界之后,什么错误都有可能发生,不一定只发生段错误或者运行错误。 所以,一定注意题目中需求的数组大小,并且多开5~10个。 在ACM竞赛中,数组越界可能会产生以下错误: Wrong Answer: 数组越界可能导致程序输出错误的结果,因为程序访问了不属于数组范围内的内存

    2024年02月02日
    浏览(72)
  • 算法竞赛备赛进阶之数位DP训练

    数位DP的思想就是对每一位进行DP,计算时记忆化每一位可以有的状态,其作用是减少运算时间,避免重复计算。 数位DP是一种计数用的DP,一般就是要统计一个区间[A,B]内满足一些条件数的个数。 以1e9甚至1e18、1e100的问题为例,因为在统计情况下有很多重复的计算,数位DP实

    2024年01月16日
    浏览(48)
  • 算法竞赛入门【码蹄集进阶塔335题】(MT2076-2100)

    为什么突然想学算法了? 用较为“官方”的语言讲 ,是因为算法对计算机科学的所有分支都非常重要。 在绝大多数的计算机科学分支领域中,要想完成任何实质性的工作,理解算法的基础知识并掌握与算法密切相关的数据结构知识是必不可少的。 但从实际而言 ,是因为当

    2023年04月19日
    浏览(46)
  • 算法竞赛入门【码蹄集进阶塔335题】(MT2281-2285)

    为什么突然想学算法了? 用较为“官方”的语言讲 ,是因为算法对计算机科学的所有分支都非常重要。 在绝大多数的计算机科学分支领域中,要想完成任何实质性的工作,理解算法的基础知识并掌握与算法密切相关的数据结构知识是必不可少的。 但从实际而言 ,是因为当

    2024年02月07日
    浏览(37)
  • 算法竞赛入门【码蹄集进阶塔335题】(MT2286-2290)

    为什么突然想学算法了? 用较为“官方”的语言讲 ,是因为算法对计算机科学的所有分支都非常重要。 在绝大多数的计算机科学分支领域中,要想完成任何实质性的工作,理解算法的基础知识并掌握与算法密切相关的数据结构知识是必不可少的。 但从实际而言 ,是因为当

    2024年02月06日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包