#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 2e5 + 5; // 数组最大长度,适配题目数据范围
ll a[N]; // 存储输入的原数组
using PII = pair<ll, ll>; // 别名,存子数组的起止位置(左边界,右边界)
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 关闭同步,加速输入输出
ll n;
cin >> n;
for (ll i = 1; i <= n; i++) { // 输入原数组,下标从1开始(符合题目常见输出习惯)
cin >> a[i];
}
// ********** 初始化算法核心变量 **********
ll m = 0; // 记录【无重复元素最长子数组】的长度,初始为0
vector<PII>p; // 存储所有【无重复元素最长子数组】的起止位置
ll p1 = 1, p2 = 1; // 滑动窗口双指针:p1=左边界,p2=右边界,初始都指向第一个元素
unordered_map<ll,ll>M; // 哈希表:key=数组元素,value=该元素在数组中【最后一次出现的下标】
// 作用:快速判断当前元素是否在窗口内重复,以及定位重复位置
// ********** 核心:滑动窗口遍历,扩展右边界,必要时收缩左边界 **********
// 遍历数组,p2作为右边界逐步向右扩展,每次处理窗口右边界的元素a[p2]
for (ll i = 1; i <= n; i++) {
p2 = i; // 步骤1:扩展滑动窗口的右边界到当前i的位置
// 步骤2:判断当前元素a[p2]是否在【当前窗口[p1,p2)】中存在(通过哈希表快速判断)
if (M.find(a[i]) == M.end()) {
// 情况1:元素未出现过/不在当前窗口内,直接记录其最新下标到哈希表
M[a[i]] = i;
} else {
// 情况2:元素已在当前窗口内重复,需要【收缩左边界】并清理哈希表
// 从原左边界p1开始,到该元素上一次出现的下标M[a[i]],逐个删除哈希表中的元素
// 原因:这些元素会被排除在新的窗口外,哈希表需保持仅存储【当前窗口内】的元素
ll j = p1;
for(; j <= M[a[i]]; j++){
M.erase(a[j]);
}
p1 = j; // 步骤3:更新滑动窗口左边界到【重复元素上一次位置+1】,保证新窗口内无重复
M[a[i]] = p2; // 步骤4:更新当前重复元素的最新下标为当前右边界p2
}
// 步骤5:更新【无重复元素最长子数组】的记录信息
ll cur_len = p2 - p1 + 1; // 计算当前窗口[p1,p2]的长度(无重复)
if (m == cur_len) {
// 情况1:当前窗口长度等于已知最长长度,加入结果集
p.emplace_back(p1, p2);
} else if (m < cur_len) {
// 情况2:当前窗口长度超过已知最长长度,更新最长长度,并重置结果集
m = cur_len; // 更新最长长度
p.clear(); // 清空原结果集(原最长的都失效了)
p.emplace_back(p1, p2); // 加入当前更长的窗口
}
// 情况3:当前窗口更短,无操作
}
// ********** 输出结果 **********
cout << p.size() << "\n"; // 输出最长无重复子数组的数量
for (auto t : p) { // 依次输出每个最长子数组的起止位置
cout << t.first << ' ' << t.second << "\n";
}
return 0;
}