树状数组与线段树

树状数组

在这里插入图片描述

线段树

在这里插入图片描述
1.父节点 ⌊ x 2 ⌋ \lfloor \frac{x}{2} \rfloor 2x x x x>>1
2.左儿子 2 x x x x x x<<1
3.右儿子 2 x x x+1 x x x
<<1|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

树状数组

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e5+7;
int a[maxn],tree[maxn<<2];
int lowbit(int x)  //返回最低位的1及其后边所有的0构成的数值
{
   
    return x&-x;
}
void add(int l,int r,int v)  //单点修改
{
   
    for(int i=l;i<=r;i+=lowbit(i)) tree[i]+=v;
}
int find(int x) //区间查询
{
   
    int res=0;
    for(int i=x;i;i-=lowbit(i)) res+=tree[i];
    return res;
}
int main()
{
   
    int n,m;
    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,n,a[i]);
    while(m--)
    {
   
        int op,l,r;
        scanf("%d%d%d",&op,&l,&r);
        if(op) add(l,n,r);
        else printf("%d\n",find(r)-find(l-1));
    }
    return 0;
}

线段树

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

using namespace std;

const int N = 100010;

int n, m;
int w[N];
struct Node
{
   
    int l, r;
    int sum;
}tree[N<<2];

void pushup(int rt)//区间和
{
   
    tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}

void build(int rt, int L, int R)   //构造线段树
{
   
    tree[rt].l=L,tree[rt].r=R;
    if (L == R)
    {
   
        tree[rt].sum=w[L];
        return ;
    }
    int mid = (L + R) >> 1;
    build(rt << 1, L, mid);  //递归构建左子树
    build(rt << 1 | 1, mid + 1, R);  //递归构建右子树
    pushup(rt);  //区间和
}

void add(int rt, int x, int v) //区间和
{
   
    int l=tree[rt].l,r=tree[rt].r;
    if (l == r)
    {
   
        tree[rt].sum += v;
        return;
    }
    int mid = (l+r) >> 1;
    if (x <= mid) add(rt << 1, x, v); //左半边
    else add(rt << 1 | 1, x, v);  //右半边
    pushup(rt);
}

int Query(int rt, int L,int R)  //查询操作
{
   
    int l=tree[rt].l, r=tree[rt].r;
    if (l >= L && r<= R) return tree[rt].sum;
    int sum=0, mid=(l+r)>>1;
    if(L<=mid) sum+=Query(rt<<1,L,R);   //和左区间有交集
    if(R>mid) sum+=Query(rt<<1|1,L,R);  //和右区间有交集
    return sum;
}

int main()
{
   
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
    build(1, 1, n);  

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

数星星

题目大意
天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。如果一个星星的左下方(包含正左和正下)有 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 <cstdio>
#include <algorithm>
using namespace std;
const int maxn=35000;
int a[maxn],tree[maxn];
int lowbit(int x)
{
   
    return x&-x;
}
void add(int l)
{
   
    for(int i=l;i<maxn;i+=lowbit(i)) tree[i]++;
}
int find(int x)
{
   
    int res=0;
    for(int i=x;i;i-=lowbit(i)) res+=tree[i];
    return res;
}
int main()
{
   
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;++i)
    {
   
        int x,y;
        scanf("%d%d",&x,&y);
        x++;  //树状数组的下标必须从1开始
        //输出a[1 ~ n],因为每个星星的等级都提高了一级
        a[find(x)]++; // a[i]+1
        add(x);   //前a[1~x]
    }
    for(int i=0;i<n;++i) printf("%d\n",a[i]);
    return 0;
}

数列区间最大值

题目大意
输入一串数字,给你 M 个询问,每次询问就给你两个数字 X,Y,要求你说出 X 到 Y 这段区间内的最大数。
输入格式
第一行两个整数 N,M 表示数字的个数和要询问的次数;
接下来一行为 N 个数;
接下来 M 行,每行都有两个整数 X,Y。
输出格式
输出共 M 行,每行输出一个数。
数据范围

1≤N≤105,
1≤M≤106,
1≤X≤Y≤N,

数列中的数字均不超过2 31 ^{31} 31−1
输入样例

10 2
3 2 4 5 6 8 1 2 9 7
1 4
3 8

输出样例

5
8
#include <cstdio>
#include <algorithm>
#include <climits>
using namespace std;
const int maxn=1e5+7;
int a[maxn];
struct node
{
   
    int l,r,maxx;
}tree[maxn<<2];
void build(int rt,int L,int R)
{
   
    tree[rt].l=L,tree[rt].r=R;
    if(L>=R)
    {
   
        tree[rt].maxx=a[L];
        return;
    }
    int mid=(L+R)>>1;
    build(rt<<1,L,mid);
    build(rt<<1|1,mid+1,R);
    tree[rt].maxx=max(tree[rt<<1].maxx,tree[rt<<1|1].maxx);
}
int find(int rt,int L,int R)
{
   
    int l=tree[rt].l,r=tree[rt].r;
    if(l>=L&&r<=R) return tree[rt].maxx;
    int mid=(l+r)>>1;   //树中的中点
    int ans=-0x3f3f3f3f;
    if(L<=mid) ans=max(ans,find(rt<<1,L,R)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幸愉信奥

谢谢亲的支持,我会继续努力啦~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值