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. linux history 想显示历史命令的时间和作者

    vi ~/.profile 增加 HISTTIMEFORMAT="%F %T `whoami` `who am i|awk '{print $1,$5}'|sed 's/ (/@/'|sed ...

  2. mysql5.7之JSON数据类型

    1.json对象 1.1.方法 使用对象操作的方法进行查询:字段->'$.json属性' 使用函数进行查询:json_extract(字段, '$.json属性') 获取JSON数组/对象长度: ...

  3. Linux下TCP/IP编程--TCP实战

    之前尝试过windows下的简单TCP客户端服务器编写,这次尝试下一下Linux环境下的TCP 客户端代码 #include <stdio.h> #include <stdlib.h ...

  4. Windows的树形目录结构

    一.文件.文件夹(目录).逻辑盘.路径的概念 · 文件:是操作系统用来存储和管理信息的基本单位.每个文件都有一个名称,叫文件名.文件名通常由‌基本文件名和‌扩展名两部分组成,其中基本文件名用于说明文件 ...

  5. Flutter TickerProvider使用

    Flutter TickerProvider使用 当需要使用Animation controller时,需要在控制器初始化时传递一个vsync参数,此时需要用到TickerProvider,例如 cl ...

  6. mysql将公司数据随机挂在部门身上

    1.创建示例数据 CREATE TABLE department_table ( company_code VARCHAR(10) COMMENT '公司编码', company_name VARCH ...

  7. 对 .NET 开发者来说,Azure AD 改名为 Microsoft Entra ID 意味着什么?

    对 .NET 开发者来说,Azure AD 改名为 Microsoft Entra ID 意味着什么? 原文地址:https://devblogs.microsoft.com/dotnet/azure ...

  8. 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-8- 元素高级定位技巧(详细教程)

    1.简介 随着网页的复杂性和动态性的增加,自动化测试变得越来越重要.Playwright作为一款强大的无头浏览器测试库,提供了多种元素定位方式,使得我们能够轻松地对网页进行自动化操作.在基础的定位方式 ...

  9. Spring AOP实例操作 简单易懂

    AOP的功能,不改变源代码可以增强类中的方法      (增强 = 代理) AOP切入点表达式: execution([权限修饰符] [返回值类型] [类全路径] [方法名称] ([参数列表])) 例 ...

  10. /etc/rancher/k3s/registries.yaml

    mirrors: "192.168.50.3": endpoint: - "https://192.168.50.3"configs: "192.16 ...