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 ...
随机推荐
- 符合ASTM标准的雨流计数法及其不同的改进方法
随着研究的深入,人们发现采用时间序列计算载荷谱太麻烦了,处理的工作量太大,我们不需要将每个时刻点的载荷都做运算,疲劳计算只需要提供幅值.均值和循环次数,鉴于此发展出了很多不同的计数方法,雨流法是最常见 ...
- Java ScheduledThreadPoolExecutor延迟或周期性执行任务
ImportNew注: 本文由新浪微博:@小飞侠_thor投稿至ImportNew.感谢@小飞侠_thor ! 如果你希望分享好的原创文章或者译文,欢迎投稿到ImportNew. Java提供的Tim ...
- js逆向之常用算法
[Python] encode & decode from urllib import parse # url进行编码与解码 url = '你好啊' url_encode = parse.qu ...
- Python消息队列之Huey
缘起: 之前在Python中使用最多的就是Celery, 同样的在这次项目中使用了Celery+eventlet的方式,但是由于具体执行的逻辑是使用的异步编写的, 当时就出现了一个问题,当使用http ...
- 新型大语言模型的预训练与后训练范式,阿里Qwen
前言:大型语言模型(LLMs)的发展历程可以说是非常长,从早期的GPT模型一路走到了今天这些复杂的.公开权重的大型语言模型.最初,LLM的训练过程只关注预训练,但后来逐步扩展到了包括预训练和后训练在内 ...
- 物联网CC2530按键单双击分别控制两灯
(1)确定思路单击和双击的效果分别是怎样的(此文章采用简单的延时函数不涉及中断).首先可以定义一个普通延时delay和一个标志位count变量,这里需有个延时阈值咱们直接可以宏定义B值(这里需要注意宏 ...
- Adobe PS 2024 软件分享 torrent
Adobe-Photoshop-2024-25.5.0.375 下载工具建议使用 qBittorrent-enhance,qBittorrent, Transmission, uTorrent 等. ...
- ARCGIS 拓扑检查步骤与修正拓扑错误技巧
转自http://xygszsh.blog.163.com/blog/static/19221517201111831348533/,写得没有说多好,贵在详细. 将数据装载如个人地理数据库,用拓扑功能 ...
- 《前端运维》二、Nginx--2请求处理流程及核心模块
前一篇内容,我们学习了nginx的一些基本概念.安装和目录的作用.这篇文章我们来学习一些更加深入的内容. 一.Nginx请求处理流程 我们先来看张图吧: 我们看上图,首先客户端请求到Nginx服务器, ...
- Qt No Target Architecture
在QT中引入processthreadsapi.h,如果出现 "No Target Architecture",需要在processthreadsapi.h前引入windows.h ...