数据结构:树状数组

树状数组

基本操作:1.快速求前缀和 2.修改一个数。

基本图示:

 lowbit:求出一个数字二进制最后一个1的位置;

原理:

我们发现,除了最后一个1,以及其后面的0,其余位置都是反,则要求最后一位1的位置,可以将原码和补码按位&;而计算机中-x有取补码,则lowbit(x)=x&-x;

#define lowbit (x&(-x));

例子:数组:1,2,3,4,5,6,7

1.如果想要修改6=0110, 

 

则会影响0110对应的:用lowbit+操作就可以修改,即6处修改,6+2(lowbit(6)=2)=8,对8处修改

2.如果想要查询【2,5】之间的和,可以求sum(5)-sum(1)

 sum(5),5=0011,则用lowbit-就可以求出,sum加上5处的值,lowbit(5)=1,5-1=4,sum再加上4处的值,刚好就是sum(5),求出【2,5】,类似于前缀和,求出sum(5)-sum(1)。

综上所述:

1.单点修改

void upload(int x,int d){
    while(x<=n){
        tree[x]+=d;
        x+=lowbit(x);
    }
}

2.区间查询

int sum(int x){
    int ans=0;
    while(x>0){
        ans+=tree[x];
        x-=lowbit(x);
    }
    return ans;
}

 一个典型题:

 

我们不难知道,如果想要求解此题,想要知道V(题中)只要知道第a_i个数,左边有m个比它大,右边有n个比它大,然后mn就是V的个数,\sum mn就是总的V图腾数,∧同理(左边,右边比它小)

     

然后利用树状数组就可以求解逆序对:

lower指比它大的数,Greater比它小的数。

第一遍求左边比它大(小)的数,第二遍(清空第一遍的tr,并逆序求一遍)求右边比它大(小)的数。

注:为啥会求出来,理由是每次更新树状数组, upload(y,1),则+1,之后遍历下一个数,就知道左边那群数比这个数大还是小的个数是多少(具体例子如下:)

1:for(int i=0;i<n;i++){
        int y=a[i];
        Greater[i]=sum(n)-sum(y);
        lower[i]=sum(y-1);
        upload(y,1);
    }
2: memset(tr, 0, sizeof tr);
    LL res1 = 0, res2 = 0;
    for (int i = n; i; i -- )
    {
        int y=a[i];
        res1+=great[i]*(LL)(sum(n)-sum(y));
        res2+=lower[i]*(LL)(sum(y-1));
        upload(y,1);
    }

 

 左边是第一次,右边是逆序第二次(上面代码1与代码2对应)

 

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

using namespace std;

const int N=200010;
int tr[N];
typedef long long LL;
#define lowbit (x&(-x));
int n;
void upload(int x,int d){
    while(x<=n){
        tr[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x){
    int ans=0;
    while(x>0){
        ans+=tr[x];
        x-=lowbit(x);
    }
    return ans;
}
int a[N],great[N],lower[N];

int main() {
    scanf("%d", &n);

    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

    for(int i=1;i<=n;i++){
        int y=a[i];
        great[i]=sum(n)-sum(y);//right
        lower[i]=sum(y-1);//left
        upload(y,1);
    }
    memset(tr, 0, sizeof tr);
    LL res1 = 0, res2 = 0;
    for (int i = n; i; i -- )
    {
        int y=a[i];
        res1+=great[i]*(LL)(sum(n)-sum(y));
        res2+=lower[i]*(LL)(sum(y-1));
        upload(y,1);
    }
    printf("%lld %lld\n", res1, res2);
    return 0;
}

3.区间修改,单点查询

利用差分数组:

 

详见差分性质 


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

using namespace std;

typedef long long LL;

const int N = 100010;

int n, m;
int a[N];
LL tr[N];

#define lowbit (x&(-x));

void upload(int x,int d){
    while(x<=n){
        tr[x]+=d;
        x+=lowbit(x);
    }
}
LL sum(int x){
    int ans=0;
    while(x>0){
        ans+=tr[x];
        x-=lowbit(x);
    }
    return ans;
}
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 ++ ) upload(i, a[i] - a[i - 1]);

    while (m -- )
    {
        char op[2];
        int l, r, d;
        scanf("%s%d", op, &l);
        if (*op == 'C')
        {
            scanf("%d%d", &r, &d);
            upload(l, d), upload(r + 1, -d);
        }
        else
        {
            printf("%lld\n", sum(l));
        }
    }

    return 0;
}

4.区间修改,区间查询

1.记录差分

   int b = a[i] - a[i - 1];

2.大致思路

如果要求区间修改,利用差分,同上。

但是,区间查询则需要利用一些数学推导:

其中,a为原数组,b为差分数组:

 总面积:(\sum_{i=1}^{x}b_i)*(x+1),减去黄色部分:\sum_{i=1}^{x} ib_i,则白色区域:(\sum_{i=1}^{x}b_i)*(x+1)-\sum_{i=1}^{x}ib_i

LL prefix_sum(int x)
{
    return sum(tr1, x) * (x + 1) - sum(tr2, x);
}

LL tr1[N];  // 维护b[i]的前缀和
LL tr2[N];  // 维护b[i] * i的前缀和
区间查询:

prefix_sum(r)-prefix_sum(l-1))

区间修改:

 add(tr1, l, d), add(tr2, l, l * d);
 add(tr1, r + 1, -d), add(tr2, r + 1, (r + 1) * -d);

 总代码:

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

using namespace std;

typedef long long LL;

const int N = 100010;

int n, m;
int a[N];
LL tr1[N];  // 维护b[i]的前缀和
LL tr2[N];  // 维护b[i] * i的前缀和

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

void add(LL tr[], int x, LL c)
{
    for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

LL sum(LL tr[], int x)
{
    LL res = 0;
    for (int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

LL prefix_sum(int x)
{
    return sum(tr1, x) * (x + 1) - sum(tr2, x);
}

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++){
        int b=a[i]-a[i-1];
        add(tr1,i,b);
        add(tr2,i,(LL)b*i);
    }
    while(m--){
        char op[2];
        int l,r,d;
        scanf("%s%d%d", op, &l, &r);
        if (*op == 'Q') {
            printf("%lld\n",prefix_sum(r)-prefix_sum(l-1));
        }
        else{
            scanf("%d", &d);
            add(tr1, l, d), add(tr2, l, l * d);
            add(tr1, r + 1, -d), add(tr2, r + 1, (r + 1) * -d);
        }
    }
    return 0;
}

5.应用:谜一样的牛

 这个题从正序入手不容易,可以从逆序入手。如何入手,可以举个例子:

 从h【4】=0开始,发现前面没有一个比它低,那么它最低,则是1。然后tree更新。

则发现,只要从最后一个开始,找到第h[i]+1个数就是它的位置,同时更新tree。

为了找到h[i]+1(其实就是求第k小的数,而tr数组记录了数组的前缀和,也就表示了数字到底是在数组中第几小),可以利用二分法。

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

using namespace std;
const int N=100010;
int n;
int h[N];
int tr[N];
int ans[N];
#define lowbit x&(-x);
void update(int x,int d){
    while(x<=n){
        tr[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x){
    int ans=0;
    while(x>0){
        ans+=tr[x];
        x-=lowbit(x);
    }
    return ans;
}
int main()
{
    cin>>n;
    for(int i=2;i<=n;i++)  cin>>h[i];
    for(int i=1;i<=n;i++) update(i,1);
    for(int i=n;i;i--){
        int k=h[i]+1;
        int l=1,r=n;
        while(l<r){
            int mid=(l+r)>>1;
            if(sum(mid)>=k) r=mid;
            else l=mid+1;
        }
        ans[i]=r;
        update(r,-1);
    }
    for (int i = 1; i <= n; i ++ ) printf("%d\n", ans[i]);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/792699.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

翻牌器单独设置前后缀样式

翻牌器单独设置前后缀样式 <template><div :style"[fontStyle,styleBackGroundColor]"><!-- <span style"color: #1d1d1d"> {{optionData}}</span>--><!-- 设置前缀样式 --><span class"prefix" …

【全面介绍Oracle】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 目录 🎥前言🎥基本概念和安装🎥SQL语言🎥PL/SQL编程🎥数据库…

【UML用户指南】-34-应用UML

目录 1、事物 1.1、结构事物 1.2、行为事物 1.3、成组事物 1.4、注释事物 2、关系 2.1、依赖 2.2、关联 2.3、泛化 3、可扩展性 4、图 4.1、结构图 4.2、行为图 5、统一过程Rational 5.1、四个阶段 5.2、九个任务 5.3、制品 5.3.1、模型 5.3.2、其他制品 利…

CACTER直播预告:SMC2全面焕新——您的邮件系统专属安全管家

在数字化的浪潮中&#xff0c;科技革命和产业变革正重塑着企业的发展轨迹。邮箱作为企业内部&#xff0c;企业和企业之间沟通的桥梁&#xff0c;其安全性和效率性是保障企业顺畅运作和信息安全的基石。 随着网络攻击手段的不断翻新&#xff0c;邮件系统所面临的安全威胁日益加剧…

医院门诊预约挂号小程序模板源码

医院门诊预约挂号小程序模板源码,主要有&#xff1a;绿色的医院住院办理&#xff0c;门诊预约挂号微信小程序页面模板。包含&#xff1a;办卡绑定、快速办理预约挂号、门诊缴费、住院服务、医院信息、个人中心、添加就诊人、找医生等等。 医院门诊预约挂号小程序模板源码

vue 画二维码及长按保存

需求 想要做如下图的二维码带文字&#xff0c;且能够长按保存 前期准备 一个canvas安装qrcode&#xff08;命令&#xff1a;npm i qrcode&#xff09; 画二维码及文字 初始化画布 <template><div><canvas ref"canvas" width"300" he…

Qt常用基础控件总结—输入部件(QComboBox类和QLineEdit)

输入部件 下拉列表控件QComboBox 类 QComboBox 类是 QWidget 类的直接子类,该类实现了一个下拉列表(组合框)。 QComboBox 类中的属性函数 1)count:const int 访问函数:int count() const; 获取组合框中的项目数量,默认情况下,对于空组合框或未设置当前项目的组合框,…

4-2 文本向量化

4-2 文本向量化 文本向量化是自然语言处理&#xff08;NLP&#xff09;中的一个关键步骤&#xff0c;通过将文本数据转化为数值向量&#xff0c;使计算机能够理解和处理自然语言。本文将深入探讨文本向量化的各种方法&#xff0c;包括词袋模型&#xff08;Bag of Words&#x…

生物素-十一聚乙二醇-沙利度胺;Biotin-PEG11-Thalidomide

Biotin-PEG11-Thalidomide&#xff0c;即生物素-十一聚乙二醇-沙利度胺&#xff0c;是一种结合了生物素、十一聚乙二醇&#xff08;PEG11&#xff09;和沙利度胺的复杂化合物。以下是对该化合物的详细分析&#xff1a; 一、组成成分及特性 生物素&#xff08;Biotin&#xff09…

备份及恢复Sonarqube服务数据

基础数据&#xff1a; 源数据机ip&#xff1a;192.*.53 测试机ip&#xff1a;192.*.65 Sonarqube访问地址&#xff1a;http://192.*.65:9000/ 账户名&#xff1a;admin 密码&#xff1a;123456 数据库postgres&#xff1a; 版本&#xff1a;PostgreSQL 15.3 一、数据备份…

厨电,被AI重构的下一个十年|产业特稿

智能化赋能下&#xff0c;厨房从闲人免进的油污重地&#xff0c;到会朋交友的社交空间。随着老板、方太等头部厨电厂商纷纷布局AI&#xff0c;厨电行业的数字化、智能化正逐渐改变了人们和烹饪之间的交互&#xff0c;重塑着厨房固有的属性、定位和职能。 作者|斗斗 编辑|皮爷…

RSA算法java实现

基于RSA算法的Java示例代码&#xff0c;展示了如何进行公钥加密、私钥解密、私钥签名和公钥验签。 非堆成加密公私钥使用学习请查看&#xff1a;非堆成加密公私钥使用-CSDN博客 代码实现 package com.chengxuyuan.demo;import javax.crypto.Cipher; import java.security.*;…

3D互动+AR试戴,赋能珠宝品牌线上营销!

随着电商浪潮的汹涌而至&#xff0c;珠宝这一传统上依赖实体店铺销售的行业&#xff0c;正积极拥抱线上转型的浪潮。然而&#xff0c;面对珠宝商品高客单价及消费者对于亲身体验的强烈需求&#xff0c;线上销售面临诸多挑战&#xff0c;尤其是图片展示难以全面展现珠宝魅力&…

cache 设计

1. cache 概念扫描 简介&#xff1a; cache 是一种小容量的缓存空间&#xff0c;类似于较小的sram 。 它的存在着重解决逻辑访问外部存储&#xff08;ddr &#xff09;的时延。 通过一种预测算法&#xff08;cache 的换入和换出&#xff09;&#xff0c;将逻辑大概率访问的热点…

Milvus核心设计(2)-----TSO机制详解

目录 背景 动机 Timestamp种类及使用场景 Guarantee timestamp Service timestamp Graceful time Timestamp同步机制 主流程 时间戳同步流程 背景 Milvus 在设计上突出了分布式的设计,虽然Chroma 也支持分布式的store 与 query。但是相对Milvus来说,不算非常突出。…

【LangChain系列】【基于Langchain的Pandascsv Agent】

目录 前言一、LangChain1-1、介绍1-2、特点 二、Pandas&csv Agent2-1、安装2-2、Pandas&csv Agent介绍2-3、Pandas&csv Agent使用2-3-1、相关库的导入&#xff1a;2-3-2、设置要调用的模型&#xff08;我这里使用阿里的模型&#xff09;2-3-3、数据读取&展示2-…

华为USG6000V防火墙v1

目录 一、实验拓扑图 二、要求 三、IP地址规划 四、实验配置 1&#x1f923;防火墙FW1web服务配置 2.网络配置 要求1&#xff1a;DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00-18:00)可以访问&#xff0c;生产区的设备全天可以访问 要求2&#xff1a;生产区不…

记一次酣畅淋漓的UDF提权(Linux)

外网打点就不放了&#xff0c;翻了一下具备suid权限的命令&#xff0c;没啥结果。 可疑的命令是/usr/lib/dbus-1.0/dbus-daemon-launch-helper但是没有找到用这个命令提权的资料。 弹shell后翻找一下源码&#xff0c;/app/api.py文件中链接了mysql&#xff0c;事出反常必有妖&…

Qt:18.状态栏(状态栏介绍、代码方式创建状态栏、在状态栏显示临时信息、在状态栏创建控件)

目录 1.状态栏介绍&#xff1a; 2.代码方式创建状态栏&#xff1a; 3. 在状态栏显示临时信息&#xff1a; 4.在状态栏创建控件&#xff1a; 1.状态栏介绍&#xff1a; Qt 状态栏是 QMainWindow 窗口的一部分&#xff0c;通常用于显示临时信息&#xff0c;如应用程序的状态、…

hbase学习

hbase学习 hbase概述&#xff1a; HBase 是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统&#xff0c;用于存储海量的结构化或者半结构化&#xff0c;非结构化的数据&#xff08;底层是字节数组做存储的&#xff09; HBase是Hadoop的生态系统之一&#xff0c;是建立在…