2024牛客暑期多校训练营7 D.Interval Selection(异或哈希+双指针)

本文主要是介绍2024牛客暑期多校训练营7 D.Interval Selection(异或哈希+双指针),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原题链接:D.Interval Selection


题目大意:


给你一个长度为 n n n 的数组 a a a,定义一个区间 [ l , r ] [l,r] [l,r] 内的连续子数组为好的,当且仅当这个子数组内的所有元素 a l , a l + 1 , . . . , a r a_{l},a_{l+1},...,a_{r} al,al+1,...,ar 在当前区间中恰好出现了 k k k 次。

例如数组 [ 1 , 1 , 2 , 3 , 2 , 3 , 1 ] [1,1,2,3,2,3,1] [1,1,2,3,2,3,1] k = 2 k=2 k=2 时,区间 [ 1 , 2 ] , [ 3 , 6 ] , [ 1 , 6 ] [1,2],[3,6],[1,6] [1,2],[3,6],[1,6] 这几个区间是好的,因为每个元素 a i a_{i} ai 都只出现了两次。而区间 [ 1 , 3 ] [1,3] [1,3] 不是好的,因为值 2 2 2 只出现了一次,同理区间 [ 1 , 7 ] [1,7] [1,7] 不是好的因为值 1 1 1 出现了三次。

问你这个数组有多少个区间是好的。

解题思路:


题解给出了线段树做法,没怎么看懂,这里给出一个更方便的异或哈希做法。

我们知道一个数字异或两次得到的值为 0 0 0,比如 3 ⊕ 3 = 0 3 \oplus3=0 33=0 1 ⊕ 7 ⊕ 7 = 0 1 \oplus 7 \oplus 7=0 177=0,但其实我们可以扩展这个性质到任意 k k k 位数异或为 0 0 0

比如当 k = 5 k=5 k=5 时,我们想要当且仅当 a i a_{i} ai 数量为 5 5 5 的倍数时异或起来为 0 0 0,且其他任意情况个 a a a 异或起来一定不为 0 0 0,我们可以做类似这样的操作。

我们知道一个数字异或两次可以归零,那么我们按照如下方法将每个 a i a_{i} ai 重新赋值。

原数组: [ a , a , a , a , a , a , a ] [a,a,a,a,a,a,a] [a,a,a,a,a,a,a] 7 7 7 a i a_{i} ai

我们随意构造一个长度为 k k k 的随机数列 B B B [ 1 , 9 , 3 , 6 , 8 ] [1,9,3,6,8] [1,9,3,6,8]

之后,按照 a 1 = 1 ⊕ 9 , a 2 = 9 ⊕ 3 , . . . , a 5 = 8 ⊕ 1 , a 6 = 1 ⊕ 9 , . . . a_{1}=1 \oplus 9,a_{2}=9 \oplus 3,...,a_{5}=8 \oplus 1,a_{6}=1 \oplus 9,... a1=19,a2=93,...,a5=81,a6=19,... 的循环顺序给对应的 a i a_{i} ai 赋值,于是我们发现了如下的情况:

修改后数组: [ 1 ⊕ 9 , 9 ⊕ 3 , 3 ⊕ 6 , 6 ⊕ 8 , 8 ⊕ 1 , 1 ⊕ 9 , 9 ⊕ 3 ] [1 \oplus 9,9 \oplus 3,3 \oplus 6,6 \oplus 8,8 \oplus 1,1 \oplus 9,9 \oplus 3] [19,93,36,68,81,19,93]

我们任选一个恰好包含了 k k k 的倍数个 a i a_{i} ai 的区间,例如区间 [ 2 , 6 ] , [ 3 , 7 ] [2,6],[3,7] [2,6],[3,7],那么我们异或的答案会为 0 0 0,而数量不为 k k k 的倍数的区间,比如 [ 1 , 2 ] , [ 4 , 4 ] [1,2],[4,4] [1,2],[4,4] 异或起来一定不为 0 0 0

可以发现,这样选取 k k k a i a_{i} ai 出来时,我们构造的数列 B B B 中的每个数字都参与了异或两次,不过这个数列要足够散乱以保证错误率达到一个极低的概率便可认为无错。

解决了 k k k 个异或为 0 0 0 的情况,假前缀异或和为 S S S,我们考虑答案如何取得。

显然我们可以枚举 r r r 的同时找到 l l l 使得 S [ r ] ⊕ S [ l − 1 ] = 0 S[r] \oplus S[l-1]=0 S[r]S[l1]=0 [ l , r ] [l,r] [l,r] 内不存在某个数字 a i a_{i} ai 的出现次数 c n t [ a i ] > k cnt[a_{i}]>k cnt[ai]>k

保证 [ l , r ] [l,r] [l,r] 内不存在某个数字 a i a_{i} ai 的出现次数 c n t [ a i ] > k cnt[a_{i}]>k cnt[ai]>k,其实只要保证我们双指针移动的同时保证 c n t [ a r ] ≤ k cnt[a_{r}] \leq k cnt[ar]k 即可保证区间内满足情况。

值得注意的是:
[ l , r ] [l,r] [l,r] 内存在 c n t [ a i ] < k cnt[a_{i}]<k cnt[ai]<k 的情况也可能存在答案,例如当 [ l , r ] [l,r] [l,r] 内形成的子数组为 k = 3 , [ 1 , 1 , 3 , 2 , 2 , 3 , 3 , 2 ] k=3,[1,1,3,2,2,3,3,2] k=3,[1,1,3,2,2,3,3,2] 时,即使 c n t 1 = 2 cnt_{1}=2 cnt1=2 也存在 [ 3 , 2 , 2 , 3 , 3 , 2 ] [3,2,2,3,3,2] [3,2,2,3,3,2] 这个子数组为答案,因此不会影响结果计算,只需要保证正确性即可。(显然)

比如数组: k = 2 , [ 2 , 1 , 1 , 2 , 1 , 2 ] k=2,[2,1,1,2,1,2] k=2,[2,1,1,2,1,2] k = 3 , [ 1 , 3 , 3 , 1 , 1 , 3 ] k=3,[1,3,3,1,1,3] k=3,[1,3,3,1,1,3] 是合法的,因为其修改后的数组的值异或为 0 0 0

但是值得注意的是当合法区间 [ l , r ] [l,r] [l,r] 形如: k = 2 , [ 1 , 1 , 2 , 2 , 3 , 3 ] k=2,[1,1,2,2,3,3] k=2,[1,1,2,2,3,3] 时,我们不能单纯让答案加 1 1 1,因为对于当前的 r r r 而言存在 3 3 3 l l l 使得 [ 1 , 1 , 2 , 2 , 3 , 3 ] , [ 2 , 2 , 3 , 3 ] , [ 3 , 3 ] [1,1,2,2,3,3],[2,2,3,3],[3,3] [1,1,2,2,3,3],[2,2,3,3],[3,3] 三个 S [ r ] ⊕ S [ l − 1 ] = 0 S[r] \oplus S[l-1]=0 S[r]S[l1]=0

即我们要进行找到区间 [ l − 1 , r − 1 ] [l-1,r-1] [l1,r1] 内有多少个下标等于 S [ r ] S[r] S[r] 的计数操作,注意是。

我们可以对 S [ r ] S[r] S[r] 开一个 v e c t o r vector vector 存下标,然后二分有多少个下标 j j j 满足 l − 1 ≤ j ≤ r − 1 l-1\leq j \leq r-1 l1jr1 即可。

构造和双指针二分用 m a p map map 辅助查询的时间复杂度均为单次操作 O ( log ⁡ n ) O(\log n) O(logn),注意一些细节即可通过。

时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)

#include <bits/stdc++.h>
#include <chrono>
#include <random>
#include <ctime>using i64 = long long;
using u64 = unsigned long long;void solve() {//随机数生成器std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());int n, k;std::cin >> n >> k;std::vector<u64> A(n + 1);for (int i = 1; i <= n; ++i) {std::cin >> A[i];}i64 ans = 0;//特判 k = 1if (k == 1) {std::map<int, int> cnt;for (int l = 1, r = 1; r <= n; ++r) {++cnt[A[r]];while (cnt[A[r]] > 1) {--cnt[A[l++]];}ans += r - l + 1;}std::cout << ans << "\n";return;}//前缀异或和std::vector<u64> S(n + 1);//统计数字 a 的出现次数(获得循环位置)std::map<int, int> cnt;//数字 a 的哈希值std::map<int, std::vector<u64>> hash;for (int i = 1; i <= n; ++i) {auto& V = hash[A[i]];if (V.size() == 0) {V.emplace_back(rng());}u64 value = V.back();if (V.size() < k) {V.emplace_back(rng());value ^= V.back();} else {int p = cnt[A[i]] % k;value = V[p] ^ V[(p + 1) % k];}++cnt[A[i]];S[i] = S[i - 1] ^ value;//构建循环后的数组}//pos 记录 l - 1 <= 的 S[r]std::map<u64, std::vector<int>> pos;cnt.clear();pos[S[0]].emplace_back(0);for (int l = 1, r = 1; r <= n; ++r) {//保证 [l, r] 内所有数字 cnt[A[i]] <= k++cnt[A[r]];while (cnt[A[r]] > k) {--cnt[A[l++]];}//边移动 r 边加入 S[r] 下标则二分大于等于 l - 1 的即可pos[S[r]].emplace_back(r);auto& P = pos[S[r]];ans += P.end() - lower_bound(P.begin(), P.end(), l - 1) - 1;}std::cout << ans << "\n";
}signed main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int t = 1;std::cin >> t;while (t--) {solve();}return 0;
}

这篇关于2024牛客暑期多校训练营7 D.Interval Selection(异或哈希+双指针)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1110233

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题

题库来源:安全生产模拟考试一点通公众号小程序 2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题是由安全生产模拟考试一点通提供,流动式起重机司机证模拟考试题库是根据流动式起重机司机最新版教材,流动式起重机司机大纲整理而成(含2024年流动式起重机司机证模拟考试题库及流动式起重机司机理论考试试题参考答案和部分工种参考解析),掌握本资料和学校方法,考试容易。流动式起重机司机考试技

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

usaco 1.3 Prime Cryptarithm(简单哈希表暴搜剪枝)

思路: 1. 用一个 hash[ ] 数组存放输入的数字,令 hash[ tmp ]=1 。 2. 一个自定义函数 check( ) ,检查各位是否为输入的数字。 3. 暴搜。第一行数从 100到999,第二行数从 10到99。 4. 剪枝。 代码: /*ID: who jayLANG: C++TASK: crypt1*/#include<stdio.h>bool h

2024网安周今日开幕,亚信安全亮相30城

2024年国家网络安全宣传周今天在广州拉开帷幕。今年网安周继续以“网络安全为人民,网络安全靠人民”为主题。2024年国家网络安全宣传周涵盖了1场开幕式、1场高峰论坛、5个重要活动、15场分论坛/座谈会/闭门会、6个主题日活动和网络安全“六进”活动。亚信安全出席2024年国家网络安全宣传周开幕式和主论坛,并将通过线下宣讲、创意科普、成果展示等多种形式,让广大民众看得懂、记得住安全知识,同时还

2024/9/8 c++ smart

1.通过自己编写的class来实现unique_ptr指针的功能 #include <iostream> using namespace std; template<class T> class unique_ptr { public:         //无参构造函数         unique_ptr();         //有参构造函数         unique_ptr(

论文翻译:arxiv-2024 Benchmark Data Contamination of Large Language Models: A Survey

Benchmark Data Contamination of Large Language Models: A Survey https://arxiv.org/abs/2406.04244 大规模语言模型的基准数据污染:一项综述 文章目录 大规模语言模型的基准数据污染:一项综述摘要1 引言 摘要 大规模语言模型(LLMs),如GPT-4、Claude-3和Gemini的快

免费也能高质量!2024年免费录屏软件深度对比评测

我公司因为客户覆盖面广的原因经常会开远程会议,有时候说的内容比较广需要引用多份的数据,我记录起来有一定难度,所以一般都用录屏工具来记录会议内容。这次我们来一起探索有什么免费录屏工具可以提高我们的工作效率吧。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  录屏软件录屏功能就是本职,这款录屏工具在录屏模式上提供了多种选项,可以选择屏幕录制、窗口

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个