【题解】CF1257C 题解
CF1257C 题解
思路分析
首先我们需要证明一个结论:首尾相同,且其中间的连续子序列( 到 )不含有其首位的数的子序列必然为可能的满足题目条件最小子序列。
证明如下:
设这个序列为 ,长度为 ,。
记这个序列的子序列 到 为 。
- 如果 中有出现次数大于 的,则此为众数。(不用担心有等于此数的出现的数在 中出现。如果有,由于 的出现次数至少为二,所以此数出现次数必然大于二,则任取出现这个数的两个位置,此连续子序列必然比该序列长度小,所以这种序列不会成为最终结果,不可能为可能的满足条件最小子序列)
- 如果 中有出现 ,则取从头到这个 出现的任一位置这一连续子序列,此连续子序列的长度比 小,不可能为可能的满足条件最小子序列。
- 否则, 为该序列唯一众数,得证。
接着继续证明:设相邻两个的 出现的位置为 ,。则从 到 这一连续子序列必然为满足上述条件的可能的满足题目条件最小子序列。
证明如下:
-
首尾相同显然。
-
若从 到 这一连续子序列中出现了 ,则设第一次出现的位置为 ,则 和 为相邻两个的 出现的位置,则 和 不为相邻两个的 出现的位置,矛盾。
-
证毕。
所以,依据上述结论,我们只需要维护一个数组,记录给定的序列中每个数上一次出现的次数,然后每次遍历到一个数,就用这个数到上个数这个连续子序列的长度去更新最小值,这个区间其实就是满足第二个结论的条件的区间,最后输出最小值即可。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#define IL inline
using namespace std;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
int a[N], p[N]; //给定数组和维护数组
void solve()
{
memset(a, 0x0, sizeof(a));
memset(p, 0x0, sizeof(p)); //多测清空
int n;
cin >> n;
for(int i = 1;i <= n;i++)
{
cin >> a[i];
}
int minv = INF; //最小值
for(int i = 1;i <= n;i++)
{
if(!p[a[i]])
{
p[a[i]] = i; //第一次出现,上一次出现的位置为这个位置
}
else
{
minv = min(minv, i - p[a[i]] + 1); //用这个数到上个数这个连续子序列的长度去更新最小值
p[a[i]] = i; //更新上一次出现的位置
if(minv == 2) break; //区间最短为 2,如果更新到了这个值就不用继续更新了,必然为最小
}
}
if(minv >= INF) minv = -1; //无解
cout << minv << endl;
}
int main()
{
int T;
cin >> T;
while(T--)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号