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

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

相关文章

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

Java中数组与栈和堆之间的关系说明

《Java中数组与栈和堆之间的关系说明》文章讲解了Java数组的初始化方式、内存存储机制、引用传递特性及遍历、排序、拷贝技巧,强调引用数据类型方法调用时形参可能修改实参,但需注意引用指向单一对象的特性... 目录Java中数组与栈和堆的关系遍历数组接下来是一些编程小技巧总结Java中数组与栈和堆的关系关于

SpringBoot集成EasyPoi实现Excel模板导出成PDF文件

《SpringBoot集成EasyPoi实现Excel模板导出成PDF文件》在日常工作中,我们经常需要将数据导出成Excel表格或PDF文件,本文将介绍如何在SpringBoot项目中集成EasyPo... 目录前言摘要简介源代码解析应用场景案例优缺点分析类代码方法介绍测试用例小结前言在日常工作中,我们经

Java中的数组与集合基本用法详解

《Java中的数组与集合基本用法详解》本文介绍了Java数组和集合框架的基础知识,数组部分涵盖了一维、二维及多维数组的声明、初始化、访问与遍历方法,以及Arrays类的常用操作,对Java数组与集合相... 目录一、Java数组基础1.1 数组结构概述1.2 一维数组1.2.1 声明与初始化1.2.2 访问

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 模板渲染原理二、模板