P1787 [入门赛 #22]非众数 Hard Version 题解

原题传送门

这里对 pjh0625 的题解进行了详细解释

1. 读题

题目要求计算给定字符串中非众数子串的数量。

非众数子串 的定义是:子串中出现次数最多的字符的频率不超过子串长度的一半。

非众数串 的定义是:一个字符串 s 中,没有任何字符的出现次数超过字符串长度的一半。

2. 解题思路

直接暴力解法(遍历所有子串并判断)的时间复杂度为 \(O(n^2)\),在 \(n \le 10^5\) 的数据范围内会超时。因此,我们需要一种更高效的算法 —— 树状数组(Binary Indexed Tree, BIT)

  • 树状数组的作用

    • 树状数组可以高效地维护和查询前缀和,时间复杂度为 \(O(\log n)\)。
    • 通过树状数组,我们可以快速统计子串中某个字符的频率。
  • 离散化处理

    • 为了处理负数索引问题,我们将每个字符的频率和位置信息离散化为正整数。
    • 具体来说,对于每个字符 c,我们使用 \(2 \times \text{sum} - j + n\) 作为树状数组的索引,其中 sum 是当前字符的频率,j 是当前索引。
  • 动态计算众数子串

    • 对于每个字符(a 到 z),遍历字符串中的每个位置。
    • 使用树状数组统计以该字符为“众数”的子串数量。
    • 最终,通过总子串数量减去众数子串数量,得到非众数子串的数量。

3. 代码逻辑

  • 初始化

    • 输入字符串并计算其长度。
    • 初始化树状数组 t 和众数子串数量 cnt
  • 遍历每个字符(a 到 z)

    • 对于每个字符 c,初始化树状数组并计算其频率。
    • 遍历字符串中的每个位置 j,动态更新树状数组。
  • 动态更新树状数组

    • 如果当前字符是目标字符,频率 sum 加 1。
    • 使用树状数组查询当前子串的贡献,并更新众数子串数量 cnt
    • 更新树状数组,将当前频率和位置信息离散化后存入树状数组。
  • 计算非众数子串数量

    • 总子串数量为 \(\frac{n(n + 1)}{2}\)。
    • 非众数子串数量 = 总子串数量 - 众数子串数量。

4. 代码分析

  • 变量定义
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 5;
typedef long long ll;
int n, t[maxn]; // n 为字符串长度,t 为树状数组
ll cnt; // 用于记录众数子串的数量
char s[maxn];
  • 自定义函数:
inline int lowbit(int x) { return x & -x; }
// 更新树状数组
inline void gx(int x) {
for (; x <= n * 3; x += lowbit(x)) t[x]++;
}
// 查询树状数组的前缀和
inline int cx(int x, int res = 0) {
for (; x; x -= lowbit(x)) res += t[x];
return res;
}
  • 输入字符串及获取长度(已进入主函数):
scanf("%s", s + 1);
n = strlen(s + 1);

用 cin 和 cout 的同学加上这两句:

// 关闭同步不流,为 cin 和 cout 加速
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
  • 遍历每个字符(a - z):
for (int i = 0; i < 26; i++) {
memset(t, 0, sizeof t); // 初始化树状数组
int sum = 0; // 当前字符的频率
// 遍历字符串中的每个位置
for (int j = 0; j <= n; j++) {
if (s[j] == i + 'a') sum++; // 如果当前字符是目标字符,频率加一
// 计算当前子串的贡献
// 2 * sum - j + n 是离散化后的值,用于避免负数
cnt += cx(2 * sum - j + n);
// 更新树状数组
gx(2 * sum - j + n + 1);
}
}
  • 输出及结束:
// 总子串数量 - 众数子串数量 = 非众数子串数量
printf("%lld", 1ll * (n + 1) * n / 2 - cnt);
return 0; // 养成好习惯,比赛时可别忘了

5. 代码展示

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 5;
typedef long long ll;
int n, t[maxn]; // n 为字符串长度,t 为树状数组
ll cnt; // 用于记录众数子串的数量
char s[maxn]; // 计算树状数组的 lowbit
inline int lowbit(int x) { return x & -x; }
// 更新树状数组
inline void gx(int x) {
for (; x <= n * 3; x += lowbit(x)) t[x]++;
}
// 查询树状数组的前缀和
inline int cx(int x, int res = 0) {
for (; x; x -= lowbit(x)) res += t[x];
return res;
} int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
// 遍历每个字符(a-z)
for (int i = 0; i < 26; i++) {
memset(t, 0, sizeof t); // 初始化树状数组
int sum = 0; // 当前字符的频率
// 遍历字符串中的每个位置
for (int j = 0; j <= n; j++) {
if (s[j] == i + 'a') sum++; // 如果当前字符是目标字符,频率加一
// 计算当前子串的贡献
// 2 * sum - j + n 是离散化后的值,用于避免负数
cnt += cx(2 * sum - j + n);
// 更新树状数组
gx(2 * sum - j + n + 1);
}
}
// 总子串数量 - 众数子串数量 = 非众数子串数量
printf("%lld", 1ll * (n + 1) * n / 2 - cnt);
return 0; // 养成好习惯
}

通过记录

看在





的份上,点个赞走吧!!!

管理员大大看在我改了这么多遍的情况下给过了吧

咱也算是熟人了

P1787 [入门赛 #22]非众数 Hard Version 题解的更多相关文章

  1. [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro]

    [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro] 实用知识 宏Macro 我们今天来讲讲Rust中强大的宏Macro. Rust的宏macro是实现元编程的强大工具. ...

  2. 【2016NOI十连赛2-2】黑暗

    [2016NOI十连赛2-2]黑暗 题目大意:定义一个无向图的权值为连通块个数的\(m\)次方.求\(n\)个点的所有无向图的权值和.多次询问. 数据范围:\(T\leq 1000,n\leq 300 ...

  3. 阿里天池 NLP 入门赛 TextCNN 方案代码详细注释和流程讲解

    thumbnail: https://image.zhangxiann.com/jung-ho-park-HbnqEhMBpPM-unsplash.jpg toc: true date: 2020/8 ...

  4. WCF入门(22)

    前言 本还想写一集WCF入门教程的,心情实在不好,明天又还有面试,改天再写吧. 说一下今天遇到的入职坑.面试能坑,上班能坑,完全没想到入职也能坑.切身经历. 今年10月份想换工作,更新了一下简历,接到 ...

  5. spring boot教程(一):入门篇(非原创,总结笔记性质)

    一,什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发 ...

  6. Spring框架入门:(非原著,转载)

    1.1.      耦合性和控制反转: 对象之间的耦合性就是对象之间的依赖性.对象之间的耦合越高,维护成本越高.因此,对象的设计应使类和构件之间的耦合最小. 例: public interface I ...

  7. Spring课程 Spring入门篇 2-2 Spring注入方式

    课程链接: 本节主要讲了以下两块内容: 1 xml两种注入方式 2 注入方式代码实现 3 特别注意 1 xml两种注入方式 构造注入和set注入 2 注入方式代码实现 2.1 set注入方式的实现 实 ...

  8. loj 数列分块入门 6 9(区间众数)

    6 题意 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及单点插入,单点询问,数据随机生成. 题解 参考:http://hzwer.com/8053.html 每个块内用一个\(vecto ...

  9. 【LGR-(-8)】洛谷入门赛 #5 题解

    比赛链接 9道题. 注:题目名称中链接为题目链接,题号中链接为比赛内链接 题目编号 洛谷题号 题目名称 题目难度 A P5713 [深基3.例5]洛谷团队系统 \(\color{red}{入门}\) ...

  10. 大爽Python入门教程 2-2 序列: 字符串、元组与列表

    大爽Python入门公开课教案 点击查看教程总目录 序列 序列(sequence): 顾名思义,有序的排列. 有序排列的一串数据. 一种容器,容器内成员有序排列. python的字符串str,元组tu ...

随机推荐

  1. 使用 LLVM 框架创建一个工作编译器,第 1 部分

    使用 LLVM 及其中间表示构建一个自定义编译器 LLVM 编译器基础架构提供了一种强大的方法来优化您使用任何编程语言编写的应用程序.了解本系列文章(由两部分组成)第一部分中有关 LLVM 的基础知识 ...

  2. 七、FreeRTOS学习笔记-中断管理

    FreeRTOS学习笔记-中断管理 中断:让CPU打断正常运行的程序,转而去处理紧急的事件(程序) 中断执行机制,可简单概括为三步: 1.中断请求:外设产生中断请求(GPIO外部中断.定时器中断等) ...

  3. golang项目之Makefile

    01  介绍 Go 提供一个名为go的命令,该命令可自动下载.构建.安装和测试 Go 包和命令. Go 提供go命令,官方的目的是为了不需要编写 Makefile,而是能够仅使用 Go 源代码本身中的 ...

  4. Ubuntu无法SSH登陆的解决方案

    1. 连接时出现提示 ssh_exchange_identification 问题提示: ssh_exchange_identification: read: Connection reset by ...

  5. qiankun 的 CSS 沙箱隔离机制

    为什么需要CSS沙箱 在 qiankun 微前端框架中,由于每个子应用的开发和部署都是独立的,将主/子应用的资源整合到一起时,容易出现样式冲突的问题 因此,需要 CSS 沙箱来解决样式冲突问题,实现主 ...

  6. 使用自定义 JsonConverter 解决 long 类型在前端的精度问题

    问题 Javascript 的 number 类型存在精度限制,浏览器反序列化 JSON 时,无法完整保留 long 类型的精度. 在 JSON 序列化时将 long 转换为 string 进行传递就 ...

  7. Postman 汉化教程

    Postman 汉化教程(Postman中文版) 迷恋自留地 postman官网下载地址 https://www.postman.com/downloads/ postman汉化包 https://g ...

  8. 远程连接利器:玩转MobaXterm

    今天这篇文章轻松不烧脑,主要是想和大家分享一下我在工作中常用的远程管理工具--MobaXterm.这款工具不仅功能强大,而且在日常的远程操作中极为高效,特别适合用来管理远程服务器.MobaXterm结 ...

  9. 第36次ccf-csp题解(思维)

    比赛链接 https://sim.csp.thusaac.com/contest/36/home   比赛感受 这会刚打完上海icpc,比起区域赛的题,这个简单太多了. 感受还不错,写的很顺手.除了第 ...

  10. 全网最详细的 tcpdump 使用指南

    https://www.cnblogs.com/wongbingming/p/13212306.html 今天要给大家介绍的一个 Unix 下的一个 网络数据采集分析工具,也就是我们常说的抓包工具. ...