数组数组 线段树 st表

树状数组 o logn 在某个位置加上某个数(如果是修改,那么就加两个数的差 ),求前缀和(求前缀和本身不支持修改),从而求区间和

奇数位置 存放的原来数组a上的位置的数
偶数位置 存放前一个奇数位置+前一个偶数位置的数
x的二进制表示有k个0
c[x]=【x-2^k ,x】
c[x]=(x-lowbit(x),x)

三个操作+初始化
lowbit
add
query查询
初始化 让tri[i]每个位置加上原来数组a[i]
int lowbit(int x){
return x&-x;
}
void add(int x,int y){//在第x个数加上y
//后面每个影响本身和本身之后后面每个和2的倍数有关的数
for(int i=x;i<=n;i+=lowbit(i))//两个lowbit都是对i操作 tri[i]+=v;
}
int query(int x){修改1到x的和
int res=0;
for(int i=x;i;i-=lowbit(i)) //两个lowbit都是对i操作
res+=tri[i];
return res;
}
初始化

数星星 给出二维平面上的 坐标求每个坐标上左下方有几个坐标表示第几级,求每个等级邮寄给坐标#

因为一层层一层给出 可以抽象成带修改+1 的前缀和,求得到的前缀和对应结果加1
因为数据输入的是按y不降输入的,而 x 可以是任意的,如果我们不实时计算,而是等到全部处理完再计算的话,会导致 “x 虽然比你大但是 y 比你小的情况”,

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 32010;
int n;
int tr[N],level[N];
int lowbit(int x)
{
    return x & -x;
}

void add(int x)  // 位置x加1
{
    for (int i = x; i <= N; i += lowbit(i)) tr[i] += 1;
}

int query(int x)  // 返回前x个数的和
{
    int res = 0;
    for (int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}



int main()
{   
    cin>>n;
    for (int i = 0; i < n; i ++ ){
    int x,y;
    scanf("%d%d",&x,&y);
    x++;//坐标操作对应x=1防止越界
    level[query(x)]++;//求x左边的数
    add(x);//坐标不重复,这个x坐标上的星星数+1
        
    }
    for (int i = 0; i < n; i ++ ) cout<<level[i]<<endl;
    return 0;
}


线段数组
单点修改
区间查询 使用最小包含

四个操作
pushup 向上更新
build 初始话
modify 修改
query 查询

父亲结点 x>>1
左儿子 x<<1 右儿子 x<<1|1 等价2*x+1

#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;
}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)//l和r需要改变l到mid  mid+1到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);//找mid的位运算向右
        pushup(u);//上面做完都只是叶子结点有值但是上面的是没有值的 因为pushup所以才从叶子找到了值
    }
}

int query(int u, int l, int r)//查询过程中l和r是不变的 变的是跟结点他所代表的值mid
{
    if (l <= tr[u].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);//查询有交集的区间 如果查询l=2 r=5 但已经查询到(1,2)代表的区间 mid=1.5 发现l向左边查 l已经被

    if (r > mid) sum += query(u << 1 | 1, l, r);//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;//注意是mid是为了找数据 所以应该在u代表的数据中找
        if (x <= mid) modify(u << 1, x, v);//分开 发现x在mid的左边就往左边递归 在右边就往右边递归
        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);
    }

    return 0;
}






区间最值 https://www.acwing.com/problem/content/1275/#

st表
注意分清楚 长度 和 端点 的关系 f[i][j]的区间是 [i, i+(2^j) -1 ]


const int N = 2e5+10,M=18;
int q[N];
int f[N][M];int n,m;

int main()
{
    cin>>n;
    for (int i = 1; i <= n; i ++ ) cin >> q[i];
    cin >> m;
    
    for (int j = 0; j < M; j ++ ){
        for (int i = 1; i+(1<<j)-1 <= n; i ++ ){
            if(!j) f[i][j]=q[i];
            else f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
    
    while (m -- ){
        int a,b;cin>>a>>b;
        int len=b-a+1;
        int k=log(len)/log(2);
        cout <<max( f[a ][k ],f[b+1-(1<<k) ][ k] )<<endl;
    }
    return 0;
}
posted @   liang302  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示
主题色彩