2021-07-11 树状数组专题

树状数组专题

写在前面:

自从我昨天更了之后,我机房的小伙伴就炸了(我(一种植物)!!牛啊!那树状数组和线段树又是什么关系呢?有兴趣安排一下树状数组吗?)

经过我抗拒了全身托更细胞后,我还是决定来更新,请大家多多支持!

概念:

树状数组:使用数组模拟树形结构,(为啥不建树呢?)没必要,能树状数组解决的东西,就没几个人会认真打树吧(不会吧,不会吧)。相对于线段树,它加乘操作更加为之简洁,实现更为之简单。这是无法与之比拟的,因此,我们也要认真学习树状数组。

还有基本常识: 查询和修改的时间复杂度均为 O(log(n))

空口无凭,我们不如去实战一番吧!

求和:
题目大意:

输入一个数列A1,A2….An(1<=N<=100000),在数列上进行M(1<=M<=100000)次操作,操作有以下两种:

(1) 格式为C I X,其中C为字符“C”,I和X(1<=I<=N,|X|<=10000)都是整数,表示把把a[I]改为X

(2) 格式为Q L R,其中Q为字符“Q”,L和R表示询问区间为L,R,表示询问A[L]+…+A[R]的值。

对于每个Q L R 的操作输出答案。

解题思路:

ok,完美的裸题,我们直接过来用树状数组敲一下代码。

首先,我们要引入一个新的东西:lowbit( int x ),这个东西求的是2进制数下最低位1的位置,主要用于二分。

很奇妙对吧?但…如何求出它呢?首先,我们看到 2进制数 时,就应该 0光一闪(位运算!)

code:

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

修改

疯狂加lowbit code:

void backspace(int p,int num)
{
    while (p<=n)
    {
        c[p]+=num;
        p+=change(p);
    }
    return;
}

查询

疯狂减lowbit code:

int question(int p)
{
    int tmp=0;
    while (p)   
    {   
        tmp+=c[p];      
        p-=change(p);   
    }   
    return tmp;
}

主程序

合并,没什么好难得,来康康!

code:

#include<bits/stdc++.h>
using namespace std;
int maxn=100010;
int n,m;int a[100010];
int c[100010];
char s[2];
int change(int p)
{
    return (p&(-p));
}
void backspace(int p,int num)
{
    while (p<=n)
    {
        c[p]+=num;
        p+=change(p);
    }
    return;
}
int question(int p)
{
    int tmp=0;
    while (p)   
    {   
        tmp+=c[p];      
        p-=change(p);   
    }   
    return tmp;
}
int main()
{   
    scanf("%d",&n);             
    for (int i=1;i<=n;i++)       
    {           
        scanf("%d",&a[i]);          
        backspace(i,a[i]);        
    }       
    scanf("%d",&m);     
    for(int i=1;i<=m;i++)        
    {       
        scanf("%s",s);          
        if (s[0]=='C')          
        {           
            int j,k;                
            scanf("%d%d",&j,&k);                
            backspace(j,k-a[j]);              
            a[j]=k; 
        }
        else           
        {           
            int l,r;            
            scanf("%d%d",&l,&r);                
            int sum=question(r)-question(l-1);                
            printf("%d\n",sum);         
        }       
    }   
    return 0;
}
星星点灯:
题目大意:

天文学家经常要检查星星的地图,每个星星用平面上的一个点来表示,每个星星都有坐标。我们定义一个星星的“级别”为给定的星星中不高于它并且不在它右边的星星的数目。天文学家想知道每个星星的“级别”。

​ 5

​ *

​ 4

​ *

​ 1 2 3

​ * * *

例如上图,5号星的“级别”是3(1,2,4这三个星星),2号星和4号星的“级别”为1。

给你一个地图,你的任务是算出每个星星的“级别”

解题思路:

ok,完美的裸题,我们直接过来变一下,定义sum,疯狂加上c数组即可。

主程序

code:

#include<bits/stdc++.h>
#define MAX 3*1e9
#define MIN -3*le9
using namespace std;
int n,m;
int lazy[100001];
int lowbit(int x)
{
	return x&(-x);
}
int question(int x)
{
	int sum=0;
	while(x!=0)
	{
		sum+=lazy[x];
		x-=lowbit(x);
	}
	return sum;
}
void backspace(int x)
{
	while(x<=32000)
	{
		lazy[x]++;
		x+=lowbit(x);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1; i<=n; i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		int z=question(x+1);
		printf("%d\n",z);
		backspace(x+1);
	}
	return 0;
}

写在最后:

树状数组简洁,便利,好用,是居家AC不二之选,让我们认真学习它把!

(不敢相信,我居然日更!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值