当前位置: 首页 > ds >正文

信息学奥赛一本通 1543:【例 3】与众不同

【题目链接】

ybt 1543:【例 3】与众不同

【题目考点】

1. RMQ问题

解决方法:

  • ST表
  • 线段树
2. 二分查找
3. 散列数组

【解题思路】

“完美序列”就是不重复元素构成的子段。以下将“完美序列”称为“不重复子段”
该题抽象后为:给定整数序列,每次查询一个区间中的最长不重复子段的长度。
设整个整数序列为a序列。
根据求最长上升子序列的经验,我们可以先求出以 a i a_i ai为结尾的最长不重复子段的长度,如果知道了子段的右端点 i i i与子段长度,子段的左端点(起始位置)自然也可以求出。
设数组 l e n len len l e n i len_i leni表示以 a i a_i ai为结尾的最长不重复子段的长度。
设数组 s t st st s t i st_i sti表示以 a i a_i ai为结尾的最长不重复子段的起始位置。
a i a_i ai为结尾的最长不重复子段一定不会包括a序列中 a i a_i ai前面的与 a i a_i ai相等的元素,需要能够取到每个元素前面与其相等的元素的下标。
设数组 l a s t last last l a s t i last_i lasti表示数值i上一次出现的下标。
注意:输入的数值范围为 ∣ a i ∣ ≤ 10 6 |a_i|\le 10^6 ai106,即 − 10 6 ≤ a i ≤ 10 6 -10^6\le a_i \le 10^6 106ai106,可能为负数。我们需要将输入的数值都增加 10 6 10^6 106,使 0 ≤ a i ≤ 2 ∗ 10 6 0\le a_i\le 2*10^6 0ai2106,这样数值就可以作为 l a s t last last数组的下标了。(或者也可以将 a a a序列离散化,或将last设为map)。
考虑求 s t i st_i sti的递推式:
首先,以 a i a_i ai为结尾的最长不重复子段的起始位置一定大于 l a s t a i last_{a_i} lastai,即大于等于 l a s t a i + 1 last_{a_i}+1 lastai+1
a i − 1 a_{i-1} ai1为结尾的最长不重复子段,肯定是由不重复元素构成的,其起始位置为 s t i − 1 st_{i-1} sti1。可以取其部分子段与 a i a_i ai构成以 a i a_i ai为结尾的最长不重复子段。

  • 如果 s t i − 1 ≥ l a s t a i + 1 st_{i-1}\ge last_{a_i}+1 sti1lastai+1,那么 s t i − 1 st_{i-1} sti1就是以 a i a_i ai为结尾的最长不重复子段的起始位置 s t i st_i sti。(因为第 s t i − 1 st_{i-1} sti1元素的前一个元素一定与以 a i − 1 a_{i-1} ai1为结尾的最长不重复子段中的某元素相同)。
  • 如果 s t i − 1 < l a s t a i + 1 st_{i-1}< last_{a_i}+1 sti1<lastai+1,那么 l a s t a i + 1 last_{a_i}+1 lastai+1就是以 a i a_i ai为结尾的最长不重复子段的起始位置 s t i st_i sti。(因为第 l a s t a i + 1 last_{a_i}+1 lastai+1元素的前一个元素 a l a s t a i a_{last_{a_i}} alastai a i a_i ai相同)。

因此有: s t i = max ⁡ { s t i − 1 , l a s t a i + 1 } st_i=\max\{st_{i-1}, last_{a_i}+1\} sti=max{sti1,lastai+1}
显然,st数组是升序的。
求出st数组后,已知最长不重复子段的右端点和左端点,自然可以求出子段长度,即len数组。 l e n i = i − s t i + 1 len_i=i-st_i+1 leni=isti+1

想要求区间 [ l , r ] [l, r] [l,r]中最长不重复子段的长度。
以区间 [ l , r ] [l, r] [l,r]中元素 a i a_i ai为结尾的最长不重复子段有两类:子段起始位置 s t i < l st_i<l sti<l,或子段起始位置 s t i ≥ l st_i\ge l stil
由于 s t st st是升序的,那么区间 [ l , r ] [l, r] [l,r]中一定存在一个位置 p p p,使得:

  • i ∈ [ l , p ] i\in[l, p] i[l,p]时, s t i < l st_i<l sti<l
  • i ∈ [ p + 1 , r ] i\in[p+1,r] i[p+1,r]时, s t i ≥ l st_i\ge l stil

在升序序列 s t st st中求满足 s t i < l st_i<l sti<l的最大的下标 p p p,可以通过二分查找完成。
找到 p p p后:

  • 对于 i ∈ [ l , p ] i\in[l, p] i[l,p],由于 s t i < l st_i<l sti<l,在 [ l , r ] [l, r] [l,r]内以 a i a_i ai为结尾的最长不重复子段的起始位置都是 l l l,自然子段 [ l , p ] [l, p] [l,p]的长度最长,长度为 p − l + 1 p-l+1 pl+1
  • 对于 i ∈ [ p + 1 , r ] i\in[p+1, r] i[p+1,r],由于 s t i ≥ l st_i\ge l stil,在 [ l , r ] [l, r] [l,r]内以 a i a_i ai为结尾的最长不重复子段的起始位置为 s t i st_i sti,长度为 l e n i len_i leni,其中最长的不重复子段的长度为 l e n len len序列区间 [ p + 1 , r ] [p+1, r] [p+1,r]的最大值。
    l e n len len序列的区间最值,可以对len序列建立ST表,每次区间查询的复杂度为 O ( 1 ) O(1) O(1)。或建立线段树,每次区间查询的复杂度为 O ( log ⁡ n ) O(\log n) O(logn),完成多次区间查询。
  • 比较 p − l + 1 p-l+1 pl+1 l e n len len序列区间 [ p + 1 , r ] [p+1,r] [p+1,r]中的最大值,求出二者的最大值,即为区间 [ l , r ] [l, r] [l,r]范围内最长不重复子段的长度。

注意:

  1. 当二分查找找到的 p = r p=r p=r时,区间 [ p + 1 , r ] [p+1,r] [p+1,r]是不存在的,查询 l e n len len序列区间 [ p + 1 , r ] [p+1,r] [p+1,r]中的最大值,应该让这一次查询返回0,这样可以不影响结果。
  2. 输入的查询区间 [ l , r ] [l, r] [l,r]的左右端点l与r都是从0开始的,本代码下标都是从1开始的,因此需要将l和r都增加1。

【题解代码】

解法1:ST表
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define LN 25
int a[N], last[2000005], st[N], len[N], f[N][LN], lg[N];
void initST(int n)
{for(int i = 2; i <= n; ++i)lg[i] = lg[i/2]+1;//lg[i]:log_2(i)向下取整 for(int i = 1; i <= n; ++i)f[i][0] = len[i];for(int j = 1; j <= lg[n]; ++j)for(int i = 1; i+(1<<j)-1 <= n; ++i)f[i][j] = max(f[i][j-1], f[i+(1<<(j-1))][j-1]);
}
int query(int l, int r)
{if(l > r)//本题中当p为r时,传入p+1, r,会出现形参l>r的情况,该情况非法,返回长度0。 return 0;int k = lg[r-l+1];return max(f[l][k], f[r-(1<<k)+1][k]);
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);int n, m, l, r;cin >> n >> m;for(int i = 1; i <= n; ++i)//变为1~n {cin >> a[i];a[i] += 1000000;//输入的数值可能为负数,数值绝对值<=1e6,加上1e6后就是非负整数。 }for(int i = 1; i <= n; ++i){ st[i] = max(st[i-1], last[a[i]]+1);//st[i]:以a[i]为结尾的最长不重复子段的起始下标位置len[i] = i-st[i]+1;//len[i]:以a[i]为结尾的最长不重复子段的长度 last[a[i]] = i;//last[i]:数值i上一次出现的下标位置}initST(n);while(m--){cin >> l >> r;l++, r++;//变为从1开始int p = lower_bound(st+l, st+r+1, l)-st-1;cout << max(p-l+1, query(p+1, r)) << '\n';//query(p, r):使用ST表求len[p]~len[r]的最大值 }return 0;
}
解法2:线段树
#include<bits/stdc++.h>
using namespace std;
#define N 200005
struct Node
{int l, r, val;
} tree[4*N];
int a[N], st[N], len[N];
map<int, int> last;
void pushUp(int i)
{tree[i].val = max(tree[2*i].val, tree[2*i+1].val);
}
void build(int i, int l, int r)
{tree[i].l = l, tree[i].r = r;if(l == r){tree[i].val = len[l];return;}int mid = (l+r)/2;build(2*i, l, mid);build(2*i+1, mid+1, r);pushUp(i);
}
int query(int i, int l, int r)
{if(tree[i].r < l || tree[i].l > r)return 0;if(l <= tree[i].l && tree[i].r <= r)return tree[i].val;return max(query(2*i, l, r), query(2*i+1, l, r));
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);int n, m, l, r, p;cin >> n >> m;for(int i = 1; i <= n; ++i)//变为1~n cin >> a[i];for(int i = 1; i <= n; ++i){ st[i] = max(st[i-1], last[a[i]]+1);//st[i]:以a[i]为结尾的最长不重复子段的起始下标位置len[i] = i-st[i]+1;//len[i]:以a[i]为结尾的最长不重复子段的长度 last[a[i]] = i;//last[i]:数值i上一次出现的下标位置}build(1, 1, n);while(m--){cin >> l >> r;l++, r++;//变为从1开始int tl = l, tr = r;//二分找满足st[i]<l的最大的i while(tl < tr){int mid = (tl+tr+1)/2;if(st[mid] < l)tl = mid;elsetr = mid-1;}p = tl;cout << max(p-l+1, query(1, p+1, r)) << '\n';//query(p, r):使用ST表求len[p]~len[r]的最大值 }return 0;
}
http://www.xdnf.cn/news/14219.html

相关文章:

  • ubuntu之坑(十四)——安装FFmpeg进行本地视频推流(在海思平台上运行)
  • UVM同步的方法
  • RPT:预训练新范式,用强化学习做预训练!
  • 生成式AI如何与RPA融合?
  • Cursor-1.0安装Jupyter-Notebook,可视化运行.ipynb文件中Python分片代码
  • 使用麒麟V10操作系统的KVM服务,但麒麟V10存在高危漏洞无法修复?
  • 【运维】iDRAC、Lifecycle Controller、Unified Server Configurator 的区别
  • 【1/2, 2/3, 3/5, 5/8, 8/13, ...写一个函数,计算以下数列的前10项之和,在主函数中调用该函数并输出结果。】2022-5-19
  • 成都鼎讯短波通信信号模拟设备:短波频段的电磁模拟王者​
  • 【iSAQB软件架构】良好的设计技术
  • spring:使用注解@Configuration、@ComponentScan创建配置类(未完待续)
  • mysql8数据库本地能连上但是远程连不上
  • AI作画提示词:Prompts工程技巧与最佳实践
  • GEO指南之内容创业者:AI时代的“品牌大模型种草”与IP推荐力打造
  • OSPF基础实验案例
  • Java登录验证后台实现详解
  • 【QSoundEffect QT 音频文件的播放】
  • 岛屿周长问题的三种解法:直接计数法、数学计算法与深度优先搜索
  • 精益数据分析(100/126):SaaS行业付费注册率优化与商业模式选择策略
  • Vue3本地存储实现方案
  • java通过hutool工具生成二维码实现扫码跳转功能
  • 【C/C++ 为什么 unique_ptr 不支持拷贝构造、赋值构造等操作】
  • SpringBoot项目接入DeepSeek指南:从零开始实现AI能力整合
  • PyTorch优化器总结
  • JS进阶 Day01
  • 前端面经整理【1】
  • 人工智能嵌入公共服务治理的风险挑战(一)
  • meshgpt 笔记2
  • 企业AI深水区突围:从星辰大海到脚下泥泞的进化论
  • 第六天 界面操作及美化(6.2 控件属性节点)