算法学习——树状数组和线段树

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

输入样例:

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.树状数组
2.线段树

代码
/*
1.输入 n:数的个数   m :操作次数  1≤n≤100000,  
  注意:下标从  1 开始
2.判断操作
3.进行操作
         0 : 区间求和
         1 :单点加数
4.输出结果

*/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1e5 + 10;
int n,m;
int w[N];

struct Node  //定义线段树的节点
{
   
    int l,r;
    int sum;
}tr[N * 4];  //线段树的数组长度大约是原数组的4倍


//查找的过程中l和r始终不改变   
//创建的过程中l和r始终在改变

void pushup(int u)
{
   
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;  //等于左孩子  +  右孩子
}

void build(int u,int l,int r)  //build  在1~n  的区间上初始化线段树————递归
{
   
    if(l == r) tr[u] = {
   l,r,w[r]};   //tr[u] = {l,r,w[r]};  这种赋值方式存在点问题  C++11能不能用
    else
    {
   
        tr[u] = {
   l,r};  //初始化  sum   为  0 
        //一层一层向下递归
        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);  //build : 在1 ~ n 区间上初始化线段树  第一个1  父节点
    
    int k,a,b;
    while(m--)
    {
   
        scanf("%d%d%d",&k,&a,&b);
        if(k == 0) printf("%d\n",query(1,a,b));   //query  求区间和
        else  modify(1,a,b);    //modify  在a的位置上加上b
    }
    return 0;
}

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 100010;

int n,m;
int a[N],tr[N];
int lowbit(int x)
{
   
    return x & (-x);
}


//创建并且加上V
void add(int x,int v)
{
   
    for(int i = x;i <= n;i += lowbit(i)) tr[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()
{
   
    int k,x,y;
    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]);  //创建并且加上v
    
    while(m--)
    {
   
        scanf("%d%d%d",&k,&x,&y);
        if(k == 0)  printf("%d\n",query(y) - query(x - 1));
        else add(x,y);
    }
    return 0;
    
}

2.数星星

题目

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

如果一个星星的左下方(包含正左和正下)有 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

分析

原始数组是 a[i] 表示横坐标 == i时的点的个数
无需体现出来
tr[i] 无意义,中间量

代码
/*
1.输入 N  :星星数目
  输入N行坐标(x,y)  y坐标正序
2.得出每一级星星的数目

*/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int N = 32010;
int n;
int tr[N
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值