五、树状数组与线段树

树状数组

定点修改,区间查询
下标从1开始
在这里插入图片描述

动态求连续区间和

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

输入格式
第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。
第二行包含 n 个整数,表示完整数列。接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。数列从 1 开始计数。

输出格式
输出若干行数字,表示 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
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int n,m;
int a[N],tr[N];

int lowbit(int x)
{
	return x&-x;
 } 
 
 void add(int x,int v)
 {
 	for(int i=x;i<=n;i+=lowbit(i))  tr[i]+=v;
 }
 
 int query(int x)
 {
 	int res=0;
 	for(int i=x;i;i-=lowbit(i))  res+=tr[i];
 	return res;
 }
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)  scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)   add(i,a[i]);
	
	while(m--)
	{
		int k,x,y;
		scanf("%d%d%d",&k,&x,&y);
		if(k==0)  printf("%d\n",query(y)-query(x-1));
		else  add(x,y);
	}
}

数星星

天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。如果一个星星的左下方(包含正左和正下)有 k颗星星,就说这颗星星是 k级的。

在这里插入图片描述

例如,上图中星星 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
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=32010;

int n;
int tr[N],level[N];

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

int add(int x)
{
	for(int i=x;i<N;i+=lowbit(i))  tr[i]++;
}

int sum(int x)
{
	int res=0;
	for(int i=x;i;i-=lowbit(i))
	  res+=tr[i];
	  return res;
 } 
 
 int main()
 {
 	scanf("%d",&n);
 	for(int i=0;i<n;i++)
 	{
	 int x,y;
 	scanf("%d%d",&x,&y);
 	x++;          //从1开始
	 level[sum(x)]++;
	 add(x); 
   }
 	for(int i=0;i<n;i++)  printf("%d\n",level[i]);
 }
 

线段树

操作一:单点修改 o(logn)
操作二:区间查询 o(logn)
在这里插入图片描述
操作
pushup():用子节点信息更新当前节点信息
build():在一段区间上初始化线段树
modify():修改
query():查询
在这里插入图片描述

动态求连续区间和

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=100010;
int n,m;
int w[N];
struct node{
	int l,r;
	int sum;
}tr[N*4];

void pushup(int u)
{
	tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;//左右儿子的和 
}


void build(int u,int l,int r)
{
	if(l==r)  tr[u]={l,r,w[r]};
	else
	{
		tr[u]={l,r};
	  int mid=l+r>>1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	pushup(u);
   }
}

int  query(int u,int l,int r)
{
	if(tr[u].l>=l&&tr[u].r<=r)  return tr[u].sum;
	int mid=tr[u].l+tr[u].r>>1;
	int sum=0;
	if(l<=mid)   sum=query(u<<1,l,r);
	if(r>mid)   sum+=query(u<<1|1,l,r);
	return sum;
}

void modify(int u,int x,int v )
{
	if(tr[u].l==tr[u].r)  tr[u].sum+=v;
	else {
		int mid=tr[u].l+tr[u].r>>1;
		if(x<=mid)  modify(u<<1,x,v);
		else modify(u<<1|1,x,v);
		pushup(u);
	}
}
int main()
{scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)  scanf("%d",&w[i]);
build(1,1,n);

 int k,a,b;
 while(m--)
 {
 	scanf("%d%d%d",&k,&a,&b);
 	if(k==0)   printf("%d\n",query(1,a,b));  //根结点编号
	 else  modify(1,a,b);    //根节点编号,插入的位置,插入的值 
 }
	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值