P1787 [入门赛 #22]非众数 Hard Version 题解
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 题解的更多相关文章
- [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro]
[易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro] 实用知识 宏Macro 我们今天来讲讲Rust中强大的宏Macro. Rust的宏macro是实现元编程的强大工具. ...
- 【2016NOI十连赛2-2】黑暗
[2016NOI十连赛2-2]黑暗 题目大意:定义一个无向图的权值为连通块个数的\(m\)次方.求\(n\)个点的所有无向图的权值和.多次询问. 数据范围:\(T\leq 1000,n\leq 300 ...
- 阿里天池 NLP 入门赛 TextCNN 方案代码详细注释和流程讲解
thumbnail: https://image.zhangxiann.com/jung-ho-park-HbnqEhMBpPM-unsplash.jpg toc: true date: 2020/8 ...
- WCF入门(22)
前言 本还想写一集WCF入门教程的,心情实在不好,明天又还有面试,改天再写吧. 说一下今天遇到的入职坑.面试能坑,上班能坑,完全没想到入职也能坑.切身经历. 今年10月份想换工作,更新了一下简历,接到 ...
- spring boot教程(一):入门篇(非原创,总结笔记性质)
一,什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发 ...
- Spring框架入门:(非原著,转载)
1.1. 耦合性和控制反转: 对象之间的耦合性就是对象之间的依赖性.对象之间的耦合越高,维护成本越高.因此,对象的设计应使类和构件之间的耦合最小. 例: public interface I ...
- Spring课程 Spring入门篇 2-2 Spring注入方式
课程链接: 本节主要讲了以下两块内容: 1 xml两种注入方式 2 注入方式代码实现 3 特别注意 1 xml两种注入方式 构造注入和set注入 2 注入方式代码实现 2.1 set注入方式的实现 实 ...
- loj 数列分块入门 6 9(区间众数)
6 题意 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及单点插入,单点询问,数据随机生成. 题解 参考:http://hzwer.com/8053.html 每个块内用一个\(vecto ...
- 【LGR-(-8)】洛谷入门赛 #5 题解
比赛链接 9道题. 注:题目名称中链接为题目链接,题号中链接为比赛内链接 题目编号 洛谷题号 题目名称 题目难度 A P5713 [深基3.例5]洛谷团队系统 \(\color{red}{入门}\) ...
- 大爽Python入门教程 2-2 序列: 字符串、元组与列表
大爽Python入门公开课教案 点击查看教程总目录 序列 序列(sequence): 顾名思义,有序的排列. 有序排列的一串数据. 一种容器,容器内成员有序排列. python的字符串str,元组tu ...
随机推荐
- 一个.NET开源、快速、功能丰富的跨平台阅读服务器
前言 今天大姚给大家分享一个基于.NET开源的快速.功能丰富的跨平台阅读服务器,它的设计初衷是提供一个全面的解决方案,满足用户的所有阅读需求.用户可以设置自己的服务器,并与朋友和家人分享阅读收藏:Ka ...
- dotnet学习笔记-专题01-异步与多线程-01
专题01 异步 多线程 1. Thread类 1.1 使用Thread创建线程 namespace ConsoleApp1; internal class Program { private stat ...
- elementUI 表格之表头合并
表头中有三个年份2018,2019和2020,每个年份下又有12个月份,后台返回的数据中每一个月份对应一个年份,类似下面这样 [{ year: '2018', month: '201801', }, ...
- PHP8新特性
PHP 8.1 提供的功能 枚举 Fiber(纤维) never 返回类型 readonly 属性 final 类常量 新的 array_is_list() 函数 新的 fsync() 和 fdata ...
- 一个.NET开源、免费、功能强大的 PDF 处理工具
前言 在日常工作中PDF文档的处理往往受限于其固有的格式,使得用户在编辑.合并.剪裁等方面面临诸多不便.今天大姚给大家分享一个.NET开源.免费.功能强大的 PDF 处理工具:PDF 补丁丁(PDFP ...
- C#使用CsRedis操作Redis
C#使用CsRedis操作Redis 转:脚本之家(https://www.jb51.net/article/201034.htm) 现在流行的redis连接客户端有StackExchange.Red ...
- 鸿蒙NEXT开发案例:九宫格随机
[引言] 在鸿蒙NEXT开发中,九宫格抽奖是一个常见且有趣的应用场景.通过九宫格抽奖,用户可以随机获得不同奖品,增加互动性和趣味性.本文将介绍如何使用鸿蒙开发框架实现九宫格抽奖功能,并通过代码解析展示 ...
- Element Plus组件v-loading在el-dialog组件上使用无效
前情 公司有经常需要做一些后台管理页面,我们选择了Element Plus,它是基于 Vue 3,面向设计师和开发者的组件库,是Vue框架生态中比较火的UI组件库,组件库丰富易用,组件链接:一个 Vu ...
- 腾讯云对象存储 COS 荣获对象存储领导力奖!!!
亚太内容分发大会暨 CDN 峰会一直致力于推动 CDN 产业深度融合发展和市场普及,现已成为亚太地区影响力最大的内容分发网络盛会. 十年来,在以腾讯云.阿里云.网宿科技等亚太 CDN 产业联盟成员孜孜 ...
- ng-alain 创建页面
https://ng-alain.com/cli/generate/zh https://ng-alain.com/docs/new-page/zh 默认情况下,创建模块 trade,创建在目录 sr ...