ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树状数组总结

2021-07-10 16:02:00  阅读:138  来源: 互联网

标签:总结 树状 int ll tree 数组 y1


1、acwing 241 楼兰图腾 树状数组-基础题

题面:相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(),他们分别用 V 的形状来代表各自部落的图腾。

西部 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n 个点,经测量发现这 n 个点的水平位置和竖直位置是两两不同的。

西部 认为这幅壁画所包含的信息与这 n 个点的相对位置有关,因此不妨设坐标分别为 ( 1 , y 1 ) , ( 2 , y 2 ) , … , ( n , y n ) ( 1 , y 1 ) , ( 2 , y 2 ) , … , ( n , y n ) , 其 中 y 1 ∼ y n 是 1 到 n 的 一 个 排 列 (1,y1),(2,y2),…,(n,yn)(1,y1),(2,y2),…,(n,yn),其中 y1∼yn 是 1 到 n 的一个排列 (1,y1),(2,y2),…,(n,yn)(1,y1),(2,y2),…,(n,yn),其中y1∼yn是1到n的一个排列。

西部 打算研究这幅壁画中包含着多少个图腾。

如果三个点$ (i,y_i),(j,y_j),(k,y_k)$满足 1 ≤ i < j < k ≤ n 1≤i<j<k≤n 1≤i<j<k≤n 且 y i > y j , y j < y k y_i>y_j,y_j<y_k yi​>yj​,yj​<yk​,则称这三个点构成 V 图腾;

如果三个点 $ (i,y_i),(j,y_j),(k,y_k)$满足 1 ≤ i < j < k ≤ n 且 y i < y j , y j > y k 1≤i<j<k≤n 且 y_i<y_j,y_j>y_k 1≤i<j<k≤n且yi​<yj​,yj​>yk​,则称这三个点构成 图腾;

西部 想知道,这 n 个点中两个部落图腾的数目。

因此,你需要编写一个程序来求出 V 的个数和 的个数。

解法: y i y_i yi​ 取值范围是1到 n的区间,事实上是求每一个点左右两边比他高的有几个,矮的有几个
相乘即可得到答案

在这里插入图片描述

关键在于如何求每一个点左右两边比他大或小的数的个数

普通做法O(n^2)

树状数组O(nlogn)

思路:从左往右遍历,一个个添加到树状数组中,随即查询比他小的数的个数即可
AC代码:
ll lowbit(ll x){
    return x & (-x);
}

ll add(ll x, ll val){           //单点增加
    for(int i=x; i<=n; i+=lowbit(i)){
        tree[i] += val;
    }
}

ll query(ll x){
    ll res = 0;
    for(int i=x; i>=1; i-=lowbit(i) ){
        res += tree[i];
    }
    return res;
}

ll sum(ll l, ll r){
    if(l > r) return 0;
    return query(r) - query(l-1);
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>y[i];
    
    //从左往右:
    memset(tree, 0, sizeof(tree));
    for(int i=1;i<=n;i++){
        left_larger[i] = sum(y[i] + 1,  n);
        left_lower[i] =  sum(1, y[i] - 1);
        add(y[i] , 1);
    }
}

2、acwing 242 树状数组 + 差分

给定长度为 N 的数列 A,然后输入 M 行操作指令。

第一类指令形如 C l r d,表示把数列中第 l∼r 个数都加上 d。

第二类指令形如 Q x,表示询问数列中第 x 个数的值。

区间修改 + 单点查询:与树状数组要求正好相反!

基于差分的思想:

差分数组: c [ i ] = a [ i ] − a [ i − 1 ] c[i] = a[i] - a[i-1] c[i]=a[i]−a[i−1]

有: a [ k ] = ∑ i = 1 k c [ i ] a[k] = \sum_{i=1}^{k}c[i] a[k]=∑i=1k​c[i]

让原数组a[i] 在 L~R 区间加上 d,即 c [ L ] + d , c [ R + 1 ] − d c[L]+d , c[R+1] - d c[L]+d,c[R+1]−d

因此:原数组的单点查询 变成了 差分数组的区间查询
原数组的区间修改 转化成了 差分数组的两个单点修改操作
  • 注意:差分数组的初始化:

  • c[i] = a[i] - a[i-1];
    
    for(int i=1;i<=n;i++){
            add(i, c[i]);
    }
    

3、acwing 244谜一样的牛 树状数组 + 二分搜索

有 n 头奶牛,已知它们的身高为 1∼n且各不相同,但不知道每头奶牛的具体身高。

现在这 n 头奶牛站成一列,已知第 i 头牛前面有 Ai 头牛比它低,求每头奶牛的身高。

输入样例:

5
0 1 2 1 0

输出样例:

2
0 4 5 3 1
思路 + 分析:
  • 从后往前看,可以得知具体身高 = 第 x + 1 矮的(从1~n未选过的里面)
  • 设置一个全为 1 的一维数组,若第x项被选了,则前缀和 = x + 1的坐标位置即为所求
  • 例如:1 0 1 1 0 0 1
  • 前缀和 1 1 2 3 3 3 4
  • 第3矮的位置是4号,因此具体身高为 4 ,所以所求为前缀和第一个值为3的位置—二分

AC代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 110000;

int n,a[N];
int bit[N], tree[N], c[N];

int lowbit(int x){
	return x & (-x);
}

int add(int x, int val){
	for(int i=x; i<=n; i+=lowbit(i)){
		tree[i] += val;
	}
}

int query(int x){
	int res = 0;
	for(int i=x;i>=1; i-=lowbit(i)){
		res += tree[i];
	}
	return res;
}

int main(){
	cin>>n;
	for(int i=2;i<=n;i++) cin>>a[i];
	
	for(int i=1;i<=n;i++){
		bit[i] = 1;
		add(i , 1); 
	}
	
	for(int i=n; i>=1; i--){
		int x = a[i] + 1;
		//二分查询bit数组前缀和为 x的第一个位置
		int l=1,r=n,mid, ans = 0;
		while(l<=r){
			mid = (l+r)/2;
			if(query(mid) >= x){
				r = mid - 1;
				ans = mid;
			}
			else l = mid + 1;
		} 
		
		add(ans, -1);
		
		c[i] = ans;
	}
	
	for(int i=1;i<=n;i++){
		cout<<c[i]<<endl;
	}
	
	return 0;
}

标签:总结,树状,int,ll,tree,数组,y1
来源: https://blog.csdn.net/m0_50796573/article/details/118635709

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有