树状数组模板题:楼兰图腾

2023-10-13 15:59

本文主要是介绍树状数组模板题:楼兰图腾,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接:https://www.acwing.com/problem/content/description/243/

题目:

在完成了分配任务之后,西部 314 来到了楼兰古城的西部。

相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(),他们分别用 V 和  的形状来代表各自部落的图腾。

西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n 个点,经测量发现这 n 个点的水平位置和竖直位置是两两不同的。

西部 314 认为这幅壁画所包含的信息与这 n 个点的相对位置有关,因此不妨设坐标分别为 (1,y1),(2,y2),…,(n,yn),其中 y1∼yn 是 1 到 n 的一个排列。

西部 314 打算研究这幅壁画中包含着多少个图腾。

如果三个点 (i,yi),(j,yj),(k,yk) 满足 1≤i<j<k≤n 且 yi>yj,yj<yk,则称这三个点构成 V 图腾;

如果三个点 (i,yi),(j,yj),(k,yk)满足 1≤i<j<k≤n 且 yi<yj,yj>yk,则称这三个点构成  图腾;

西部 314 想知道,这 n 个点中两个部落图腾的数目。

因此,你需要编写一个程序来求出 V 的个数和  的个数。

输入格式

第一行一个数 n。

第二行是 n个数,分别代表 y1,y2,…,yn。

输出格式

两个数,中间用空格隔开,依次为 V 的个数和  的个数。

数据范围

对于所有数据,n≤200000,且输出答案不会超过 int64。
y1∼yn 是 1 到 n 的一个排列。

输入样例:

5
1 5 3 2 4

输出样例:

3 4

思路:

暴力的方式:

求 ∧的个数

求i左边比a[i]小的值,以及i右边,比a[i]小的值。 然后乘起来,

同样 i + 1的也是如此, i + 2的也是如此。

然后每个对应i上能够组成的总和加起来就是答案。  

V 的个数也是一样。

暴力求的话,时间复杂度为O(n ^ 2).

举例来说明树状数组的用途:将原先O(n)求某段区间的和,变为了O(logn)求这个区间的和。但本质没变,都是求a[1] + a[2] + ....+ a[n]的和。

如存入3.则c[3] += 1,c[4] += 1, c[8] += 1.

而当我们求解1~4的和时, 输出的就是c[4]. c[4]的含义就是以4为终点,长度为lowbit(4)也就是4 的和。(即4,3,2,1出现的总次数)。

而当我们求解2~4的和时,就使用c[4] - c[1]即可。所以依然为1, 因为只有3出现了并只出现了1次。

画图举例:

所以求某个i左边区间中比a[i]小的数个数总和,其实就是求左边这个区间在1~a[i] - 1中数值出现的次数和。 所以将a[1~i-1]的值出现的次数存入到树状数组中, 

如i == 3 , a[1] = 5, a[2] = 6, a[3] = 10,

由 a[1] == 5, 所以对应的5出现次数 + 1,从而树状数组改变,c[5] += 1, c[6] += 1,c[8] += 1.

a[2] == 6,所以对应的6出现次数 + 1, 从而树状数组改变, c[6] += 1, c[8] += 1.

到了a[3]的时候,所以求 1~10 - 1这些数值出现的次数。 于是sum(9) - sum(1 - 1)

c[9] + c[8] - c[0] = 2.

所以可以看出实际上还是求1~9出现次数的和,但是从原先的暴力(1出现次数 + 2出现次数+...+9出现的次数)   优化为了 c[9] + c[8] - c[0]. 

这就是树状树组的优化,由原先的O(n)求区间和, 变为了O(logn)求区间和。

而每修改一次 某个a[]出现的次数 就修改一次c[],主要是因为后面需要通过c[]求区间和,所以修改。

所以是否可以用树状数组对我们每次的暴力求左边区间小于a[i]的次数和进行优化呢?实际上是可以的。

以求 ∧的个数为例,

求i左边的1~i - 1的区间中,小于 a[i]这个值的出现次数和。 

每一次遍历i,都将对应的a[i]值加入到对应树状数组中,也就是a[i]这个值出现次数 + 1,从而使得c[]改变, 当到达了a[i]时,就通过树状数组在O(logn)的时间内求出 1~a[i - 1]出现的总次数。使用sun(a[i - 1]) - sum(1 - 1).

求i左边的1 ~ i - 1的区间中,大于a[i]这个值的出现次数的和。也同上,每次将a[i]的值存入到树状数组中,也就是a[i]值出现次数 + 1, 从而使得c[]改变,当到达a[i]时, 就通过树状数组求

a[i] + 1 ~ n出现的总次数。 sum(n) - sum(a[i] + 1).

从而用树状数组求解所有 i左边 比a[i]小的次数 t1,比a[i]大的次数t2, 右边比a[i]小的次数t3,比a[i]大的次数t4.

每一个i的t1 * t3 的总和。

每一个i的 t2 * t4的总和。

总结:

当求某个区间的总和 并且 需要 单点修改某个点的值时,看是否可以使用树状数组进行优化。

代码实现:

/*
c[i]的含义就是 以i为终点,长度为lowbit(i)的前缀和
*/
# include <iostream>
# include <cstring>
using namespace std;const int N = 200010;int a[N];
int c[N];int lower[N]; //表示左边比第i个位置小的数的个数
int up[N];  //表示左边比第i个位置大的数的个数int n;int lowbit(int x)
{return x & -x;
}void add(int x, int k)
{for(int i = x; i <= n; i += lowbit(i)) c[i] += k;
}int sum(int x)
{int res = 0;for(int i = x ; i ; i -= lowbit(i)){res += c[i];}return res;
}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];lower[i] = sum(y - 1);  // 找i的左边, 所有 1 ~ y - 1的总个数up[i] = sum(n) - sum(y);  // 找到i的左边 所有 y + 1 ~ n的总个数add(y,1);}memset(c,0,sizeof c); // 将c清空long long v = 0;long long d = 0;for(int i = n ; i >= 1 ; i--){int y = a[i];d += (long long)sum(y - 1) * lower[i];v += (long long)( sum(n) - sum(y) ) * up[i];add(y,1);}printf("%lld %lld\n",v,d);return 0;
}

这篇关于树状数组模板题:楼兰图腾的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/204339

相关文章

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

JAVA数组中五种常见排序方法整理汇总

《JAVA数组中五种常见排序方法整理汇总》本文给大家分享五种常用的Java数组排序方法整理,每种方法结合示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录前言:法一:Arrays.sort()法二:冒泡排序法三:选择排序法四:反转排序法五:直接插入排序前言:几种常用的Java数组排序

Java如何根据word模板导出数据

《Java如何根据word模板导出数据》这篇文章主要为大家详细介绍了Java如何实现根据word模板导出数据,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... pom.XML文件导入依赖 <dependency> <groupId>cn.afterturn</groupId>

Python中Flask模板的使用与高级技巧详解

《Python中Flask模板的使用与高级技巧详解》在Web开发中,直接将HTML代码写在Python文件中会导致诸多问题,Flask内置了Jinja2模板引擎,完美解决了这些问题,下面我们就来看看F... 目录一、模板渲染基础1.1 为什么需要模板引擎1.2 第一个模板渲染示例1.3 模板渲染原理二、模板

利用Python打造一个Excel记账模板

《利用Python打造一个Excel记账模板》这篇文章主要为大家详细介绍了如何使用Python打造一个超实用的Excel记账模板,可以帮助大家高效管理财务,迈向财富自由之路,感兴趣的小伙伴快跟随小编一... 目录设置预算百分比超支标红预警记账模板功能介绍基础记账预算管理可视化分析摸鱼时间理财法碎片时间利用财

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注