【树状数组专题】【蓝桥杯备考训练】:数星星、动态求连续区间和、一个简单的整数问题、一个简单的整数问题2【已更新完成】

news2024/11/27 8:26:03

目录

1、数星星(《信息学奥赛一本通》 & ural 1028)

思路:

基本思路:

树状数组经典三函数:

1、lowbit()函数

2、query()函数

3、add()函数

最终代码:

2、动态求连续区间和(《信息学奥赛一本通》 & 模板)

思路:

代码:

三个主要函数:

1、lowbit()函数

2、query()函数

3、add()函数

最终代码: 

3、一个简单的整数问题(《算法竞赛进阶指南》)

思路:

代码:

4、一个简单的整数问题2(POJ 2468 & 《算法竞赛进阶指南》 & kuangbin专题)

思路:

改写后的add函数:

presum函数:

最终代码:


1、数星星(《信息学奥赛一本通》 & ural 1028)

天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。

本题采用数学上的平面直角坐标系,即 x 轴向右为正方向,y 轴向上为正方向。

如果一个星星的左下方(包含正左和正下)有 k 颗星星,就说这颗星星是 k 级的。

1.png

例如,上图中星星 5 是 3 级的(1,2,4 在它左下),星星 2,4 是 1 级的。

例图中有 1 个 0 级,2 个 1 级,1 个 2 级,1 个 3 级的星星。

给定星星的位置,输出各级星星的数目。

换句话说,给定 N 个点,定义每个点的等级是在该点左下方(含正左、正下)的点的数目,试统计每个等级有多少个点。

输入格式

第一行一个整数 N,表示星星的数目;

接下来 N 行给出每颗星星的坐标,坐标用两个整数 x,y表示;

不会有星星重叠。星星按 y 坐标增序给出,y 坐标相同的按 x 坐标增序给出。

输出格式

N 行,每行一个整数,分别是 0 级,1 级,2 级,……,N−1级的星星的数目。

数据范围

1≤N≤15000
0≤x,y≤32000

输入样例:
5
1 1
5 1
7 1
3 3
5 5
输出样例:
1
2
1
1
0
思路:
基本思路:

由于x和y都是增序的,这意味每一次增加的星星都出现在原来星星的"右上方"

基于这个信息,我们可以发现对于每个星星每次都可以进行"查询",因为后插入的星星对其没有影响,快速地实现插入和查询两个操作:树状数组

查询后用一个level数组存储每个等级星星的数量(不算上自己)

最后把星星本身插入到树状数组中

树状数组经典三函数:

1、lowbit()函数
int lowbit(int x)
{
    return x&-x;
}
2、query()函数
int query(int x)
{
	//query表示查询1~x的总和
	int res=0;
	for(int i=x;i!=0;i-=lowbit(i))
	{
		res+=tree[i];
	}
	return res;
}
3、add()函数
//add表示在某一个位置加上一个数
void add(int x,int v)
{
	for(int i=x;i<Max;i+=lowbit(i))
	{
		tree[i]+=v;
	}
 } 
最终代码:
#include<bits/stdc++.h>
//需要快速完成两个操作 
//1、0~x中数的个数(优化:如果一个数出现过就是1,这样可以把求前缀的个数转化为求前缀和) 
//2、添加一个数x 

//根据特定的需求选择特定的数据结构

//本题选择树状数组(可以快速支持这两个操作)(线段树也可以) 
//树状数组能操作的线段树都能操作 
 
//树状数组的求解,下标要从1开始 
using namespace std;

const int N=15000+10; 

const int Max=32010;//坐标最大值 

int n;


int level[N],tree[Max];//答案、树状数组 

//树状数组的三个函数

int lowbit(int x)
{
	return x&-x;//返回的是x的二进制表示中最后一位1 
} 

int query(int x)
{
	//query表示查询1~x的总和
	int res=0;
	for(int i=x;i!=0;i-=lowbit(i))
	{
		res+=tree[i];
	}
	return res;
}

//add表示在某一个位置加上一个数
void add(int x,int v)
{
	for(int i=x;i<Max;i+=lowbit(i))
	{
		tree[i]+=v;
	}
 } 

int main()
{
	cin>>n;
	
	for(int i=0;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		x++;//树状数组下标从1开始 
		int t=query(x);//统计一下1~x的数的总和 (也就是横坐标范围为1~x的星星的数量) 
		level[t]++;//这个等级的星星数++; 
		add(x,1);//把当前数加到树状数组当中 
	}
	
	for(int i=0;i<n;i++)
	{
		printf("%d\n",level[i]);
	 } 
	
	return 0;
} 
//树状数组能快速的求前缀和O(log n)
//能快速修改某一个数O(log n) 

2、动态求连续区间和(《信息学奥赛一本通》 & 模板)

给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。

输入格式

第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。

第二行包含 n 个整数,表示完整数列。

接下来 m 行,每行包含三个整数 k,a,b(k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。

数列从 11 开始计数。

输出格式

输出若干行数字,表示 k=0 时,对应的子数列 [a,b]的连续和。

数据范围

1≤n≤100000
1≤m≤100000
1≤a≤b≤n
数据保证在任何时候,数列中所有元素之和均在 int 范围内。

输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8
输出样例:
11
30
35
思路:

边插入边查询就,用树状数组再合适不过了,也是标准的模板

代码:
三个主要函数:
1、lowbit()函数
int lowbit(int x)
{
    return x&-x;
}
2、query()函数
int query(int x)
{
	//query表示查询1~x的总和
	int res=0;
	for(int i=x;i!=0;i-=lowbit(i))
	{
		res+=tree[i];
	}
	return res;
}
 
3、add()函数
//add表示在某一个位置加上一个数
void add(int x,int v)
{
	for(int i=x;i<Max;i+=lowbit(i))
	{
		tree[i]+=v;
	}
 } 
最终代码: 
//树状数组中所有的奇数都和原数组相等 

#include<bits/stdc++.h>

using namespace std;

const int Max=1e6+10;

const int N=1e6+10;

int tree[Max];

//树状数组可以解决: 
//某个位置上的数,加上一个数---单点修改
//求某一个前缀和---区间查询 

//+差分=区间修改 单点查询 || 区间修改 区间查询 

//前缀和数组不支持修改,只支持查询 
//lowbit(x)=2**k(k为末尾连续0的个数) 

// 树状数组每个位置存的都是原数组一段数的和(从x-lowbit(x)到x)
//c[x]=value[x-lowbit(x) ,x]     

//树状数组的三个操作 
int lowbit(int x)
{
	return x&-x;
}

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

int add(int x,int v)//在某一个位置x加上v (会影响到后面的树根,所以有如下写法) 
{
	for(int i=x;i<=Max;i+=lowbit(i))
	{
		tree[i]+=v;
	}
}

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	
	for(int i=1;i<=n;i++)
	{
		int v;
		scanf("%d",&v);
		add(i,v);
	}

	while(m--)
	{
		int op,a,b;
		scanf("%d%d%d",&op,&a,&b);
		
		if(op==0)
		{
			int res=query(b)-query(a-1);
			printf("%d\n",res);
		}
		else
		{
			add(a,b);
		}
	}
	return 0;
} 

3、一个简单的整数问题(《算法竞赛进阶指南》)

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

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

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

对于每个询问,输出一个整数表示答案。

输入格式

第一行包含两个整数 N 和 M。

第二行包含 N 个整数 A[i]。

接下来 M 行表示 M 条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围

1≤N,M≤1e5
|d|≤10000
|A[i]|≤1e9

输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4
Q 1
Q 2
C 1 6 3
Q 2
输出样例:
4
1
2
5
思路:

树状数组+差分的应用:差分使得树状数组由单点修改+区间查询进化为了:区间修改+单点查询

经典三操作不变,只是求出来的和变成了原数组而已

代码:
  //树状数组中所有的奇数都和原数组相等 

#include<bits/stdc++.h>

using namespace std;

const int Max=1e6+10;

const int N=1e6+10;

int tree[Max];

//树状数组可以解决: 
//某个位置上的数,加上一个数---单点修改
//求某一个前缀和---区间查询 

//+差分=区间修改 单点查询 || 区间修改 区间查询 

//前缀和数组不支持修改,只支持查询 
//lowbit(x)=2**k(k为末尾连续0的个数) 

// 树状数组每个位置存的都是原数组一段数的和(从x-lowbit(x)到x)
//c[x]=value[x-lowbit(x) ,x]     

//树状数组的三个操作 
int lowbit(int x)
{
	return x&-x;
}

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

int add(int x,int v)//在某一个位置x加上v (会影响到后面的树根,所以有如下写法) 
{
	for(int i=x;i<=Max;i+=lowbit(i))
	{
		tree[i]+=v;
	}
}

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	
	for(int i=1;i<=n;i++)
	{
		int v;
		scanf("%d",&v);
		add(i,v);
	}

	while(m--)
	{
		int op,a,b;
		scanf("%d%d%d",&op,&a,&b);
		
		if(op==0)
		{
			int res=query(b)-query(a-1);
			printf("%d\n",res);
		}
		else
		{
			add(a,b);
		}
	}
	return 0;
} 

4、一个简单的整数问题2(POJ 2468 & 《算法竞赛进阶指南》 & kuangbin专题)

给定一个长度为 N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:

  1. C l r d,表示把 A[l],A[l+1],…,A[r]都加上 d。
  2. Q l r,表示询问数列中第 l∼r个数的和。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数 N,M。

第二行 N 个整数 A[i]。

接下来 M 行表示 M 条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围

1≤N,M≤1e5
|d|≤10000
|A[i]|≤1e9

输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例:
4
55
9
15
思路:

树状数组+差分

这里需要一个小小的推导,最后得出公式:

Sn=(∑bi) * (n+1) - (∑(i*bi))) 记住即可

为了实现这个公式,维护两个数组,一个是差分树状数组tr1,另一个是存储i*tr[i]的树状差分数组

由于要为两个数组进行add操作,所以我们改写add函数(加一个参数)

改写后的add函数:
void add(LL tr[],int x,LL v)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		tr[i]+=v;	
	}	
} 

为了实现公式,我们写出求Sn的函数:

presum函数:
LL presum(int x)//求前缀和Sx(x及之前的和) (Sn=(∑bi) * (n+1) - (∑(i*bi))) 
{
	LL a = query(tr1,x)*(x+1);
	LL b=query(tr2,x);
	return a-b;
}
最终代码:
#include<bits/stdc++.h>

using namespace std;

const int N=1e5+10;

typedef long long LL;

int a[N];//a是原数组 

LL tr1[N];//维护b【i】的前缀和 

LL tr2[N];//维护i*b【i】的前缀和

int n,m; 

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

//这里因为要处理两个数组,所以加上数组参数
void add(LL tr[],int x,LL v)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		tr[i]+=v;	
	}	
} 

LL query(LL tr[],int x)
{
	LL res=0;
	for(int i=x;i;i-=lowbit(i))
	{
		res+=tr[i];
	}
	return res;
}

LL presum(int x)//求前缀和Sx(x及之前的和) (Sn=(∑bi) * (n+1) - (∑(i*bi))) 
{
	LL a = query(tr1,x)*(x+1);
	LL b=query(tr2,x);
	return a-b;
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	
	//形成树状差分数组 
	for(int i=1;i<=n;i++)
	{
		int b=a[i]-a[i-1];
		add(tr1,i,b);
		add(tr2,i,(LL)b*i);
	}	
	
	while(m--)
	{
		char op[1];
		scanf("%s",op);
		if(op[0]=='C')
		{
			int l,r,v;
			scanf("%d%d%d",&l,&r,&v);
			
			//b[l]+=v b[r+1]-=v
			add(tr1,l,v);add(tr1,r+1,-v);
			//b[l]+=l*v b[r+1]-=l*v
			add(tr2,l,(LL)l*v);add(tr2,r+1,(LL)-(r+1)*v);
			
		}
		else
		{
			int l,r;
			scanf("%d%d",&l,&r);
			
			cout<<presum(r)-presum(l-1)<<endl;
		}
	}
	
	return 0;
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1560888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能仪器替代技术工程师重复工作 专注生产方案优化!

关键词&#xff1a;智能仪器,测径仪,测宽仪,测厚仪,直线度测量仪 在当今竞争激烈的市场环境下&#xff0c;企业需要不断提高生产效率和质量&#xff0c;以满足客户的需求。而技术工程师在生产过程中扮演着至关重要的角色&#xff0c;但他们的时间和精力往往被重复的工作所占据&…

FPGA寄存器 Vivado IP核

1. 今日任务 小梅哥视频&#xff1a;21A 认识并理解FPGA中的存储器模型_哔哩哔哩_bilibili 21B 学习使用Vivado中的存储器资源_哔哩哔哩_bilibili 2. FPGA中的存储器类型 只读ROM eg.CMOS摄像头初始化、DDS信号发生器(固定波形数据的…

docker-compse安装es(包括IK分词器扩展)、kibana、libreoffice

Kibana是一个开源的分析与可视化平台&#xff0c;设计出来用于和Elasticsearch一起使用的。你可以用kibana搜索、查看存放在Elasticsearch中的数据。 Kibana与Elasticsearch的交互方式是各种不同的图表、表格、地图等&#xff0c;直观的展示数据&#xff0c;从而达到高级的数据…

【NOI】树 2164 - 子结点的数量 2165 - 子结点的数量(2)1775 - 谁的孙子最多1776 - 谁的孙子最多II

文章目录 问题一&#xff1a;2164 - 子结点的数量问题二&#xff1a;2165 - 子结点的数量&#xff08;2&#xff09;问题三&#xff1a;1775 - 谁的孙子最多问题四&#xff1a;1776 - 谁的孙子最多II 问题一&#xff1a;2164 - 子结点的数量 类型&#xff1a;树 题目描述&…

mysql 故障排除与优化

目录 一 mysql 逻辑架构图 二 msql单实例常见故障 1, 故障一 2, 故障二 3, 故障三 4, 故障四 5, 故障五 6, 故障六 7, 故障七 8, 故障八 三 mysql 主从环境常见故障 1, 故障一 2, 故障二 3, 故障三 四 mysq 常见优化 1&#xff0c;硬件优化…

校园二手书交易|基于springboot的校园二手书交易设计与实现(附项目源码+论文)

基于springboot校园二手书交易平台的设计与实现 目录 基于springboot校园二手书交易平台的设计与实现 一、摘要 二、相关技术 java、tomcat、mysql、spring、sprigBoot、mybatis、query、vue 三、系统设计 3.1 整体功能设计图 3.2 功能具体细节设计 四、数据库设计 五…

砍树c++

题目&#xff1a; 代码&#xff1a; #include<bits/stdc.h> using namespace std; long long n,m,a[100000005]; bool jltm(int x){long long sum0;for(int i1;i<n;i){if(a[i]>x) sumsuma[i]-x;}//计算此时锯片高度砍掉的木材if(sum>m) return 1;else return 0…

城市排涝与海绵城市教程

原文链接&#xff1a;城市排涝与海绵城市教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247599633&idx3&snbfe71e1b4f918864fdfe8e33db8a104b&chksmfa8207f6cdf58ee03dea20cc64bb106aadd729fcbf0a1ce3f920d3e767f78e53fa6a094d8d2a&token21066…

Linux_应用篇(02) 文件 I/O 基础

本章给大家介绍 Linux 应用编程中最基础的知识&#xff0c;即文件 I/O&#xff08;Input、 Outout&#xff09; &#xff0c; 文件 I/O 指的是对文件的输入/输出操作&#xff0c;说白了就是对文件的读写操作&#xff1b; Linux 下一切皆文件&#xff0c;文件作为 Linux 系统设计…

微信小程序wx.navigateTo无法跳转到Component组件问题解决。(共享元素动画必备)

关于Component构造器官方是有文档说明的&#xff0c;然后官方文档内部也给出了组件是可以通过像pages一样跳转的。但是官方文档缺少了必要的说明&#xff0c;会引起wx.navigateTo无法跳转到组件问题&#xff01; 以下是官方文档截图&#xff1a; 解决方式&#xff1a; 组件创建…

IP归属地在互联网行业中的应用

摘要&#xff1a;IP&#xff08;Internet Protocol&#xff09;地址归属地是指互联网上某个IP地址所对应的地理位置信息。在互联网行业中&#xff0c;IP归属地具有重要的应用价值&#xff0c;包括网络安全、广告定向、用户定位等方面。IP数据云将探讨IP归属地在互联网行业中的应…

解锁流量密码:自养号测评助亚马逊、shein店铺销量飙升

流量是影响一款产品销售的重要因素&#xff0c;当一款产品的流量增多&#xff0c;那么其销量也会随之增加&#xff0c;相反的&#xff0c;流量减少&#xff0c;那么销量也会随之降低。那卖家们又该如何提高店铺流量呢&#xff1f;下面我们就来分享一些提高店铺流量的方法。 无…

入门stm32单片机,0基础是怎么做到的

大家好&#xff0c;今天给大家介绍8步就能系统入门stm32单片机&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 对于没有基础的大学生来说&#xff0c;想要系统入门STM32单片机&a…

校园招聘管理系统(源码+文档)

校园招聘管理系统&#xff08;小程序、ios、安卓都可部署&#xff09; 文件包含内容程序简要说明含有功能项目截图客户端热门岗位校园招聘首页个人简历添加个人简历我的界面注册界面查看个人简历界面个人资料界面登录界面消息界面退出登录 管理端登录界面![请添加图片描述](htt…

GLTFExporter是一个用于将3D场景导出为glTF格式的JavaScript库。

demo案例 GLTFExporter是一个用于将3D场景导出为glTF格式的JavaScript库。下面我将逐个讲解其入参、出参、属性、方法以及API使用方式。 入参&#xff08;Input Parameters&#xff09;: GLTFExporter的主要入参是要导出的场景对象和一些导出选项。具体来说&#xff1a; s…

JRT业务开发起步

这是一段充满挑战与奋斗的旅程&#xff0c;自第一行Java代码的写下起&#xff0c;便历经重重险阻。从细微的代码行&#xff0c;逐步汇聚成实用的工具类方法&#xff1b;从工具类方法的积累&#xff0c;逐渐构建起功能强大的工具包&#xff1b;再从工具包的整合&#xff0c;最终…

python Cyber_record 包 一些命令

python Cyber_record 包 1. 查看record文件的统计信息 cyber_record info -f 20231101211111.record.00005 c record_file: 20231101211111.record.00005 version: 1.0 begin_time: 2023-11-01 21:13:48.099650 end_time: 2023-11-01 21:14:19.267622 duration: 3…

jenkins手把手教你从入门到放弃01-jenkins简介(详解)

一、简介 jenkins是一个可扩展的持续集成引擎。持续集成&#xff0c;也就是通常所说的CI&#xff08;Continues Integration&#xff09;&#xff0c;可以说是现代软件技术开发的基础。持续集成是一种软件开发实践&#xff0c; 即团队开发成员经常集成他们的工作&#xff0c;通…

STM32L4R9 的 QuadSPI Flash 通讯速率不理想

1. 引言 客户反应 STM32L4R9 同 QSPI Flash 通讯&#xff0c;测出来的读取速率为 10MB/s&#xff0c; 和理论值相差较大。 2. 问题分析 按照客户的时钟配置和 STM32L4R9 的数据手册中的数据&#xff0c;OSPI 读数速率为 10MB/s 肯定存在问题。同时我们也可以在 AN4760 应用手…

SharedPreferences.Editor 中 apply 与 commit 方法的区别

在 Android 开发中&#xff0c;SharedPreferences 是我们用来存储简单键值对数据的工具。这就像是在口袋里带着一个小笔记本&#xff0c;随时记录下要点或标记。但当涉及到保存这些笔记时&#xff0c;你有两个选择&#xff1a;apply 或 commit。它们乍看之下似乎可以互换使用&a…