题意简述
给定一个序列,长度为 n ( < = 1 e 5 ) n(<=1e5) n(<=1e5),所有数都在 [ 1 , k ] [1,k] [1,k]范围内。请你在序列中找到一个长度最短的没有出现的子序列(不一定连续的那种)。
思路
我们把原序列分组(一组是一段连续的子序列),一个组里包含 [ 1 , k ] [1,k] [1,k]中的所有数。
不知道这个奆佬是怎么想到这个东西的。奆佬太强了%%%
加上一些必要的注释:
第一组的左端点是 1 1 1,在分组过程中,左端点=上一个组的右端点 + 1 +1 +1,在包含 [ 1 , k ] [1,k] [1,k]所有数的前提下,保证右端点最小(就是找到第一个满足的就 b r e a k break break)。
比如说样例是
1
,
5
,
3
,
2
,
5
,
1
,
3
,
4
,
4
,
2
,
5
,
1
,
2
,
3
1,5,3,2,5,1,3,4,4,2,5,1,2,3
1,5,3,2,5,1,3,4,4,2,5,1,2,3,分组就是
[
1
,
5
,
3
,
2
,
5
,
1
,
3
,
4
]
,
[
4
,
2
,
5
,
1
,
2
,
3
]
[1,5,3,2,5,1,3,4],[4,2,5,1,2,3]
[1,5,3,2,5,1,3,4],[4,2,5,1,2,3]。
这个分组过程是 O ( n ) O(n) O(n)的。然后由于我们保证了右端点最小,所以,每个组的最后一个数在这个组中肯定是有且仅有 1 1 1次出现。
简单的证明:如果最后一个数 x x x有多次出现,除了在最后一个位置,在前面也有几次。那为什么我们要选这最后一个位置呢?我们珂是要保证右端点最小的!综上,没有这样的 x x x,证毕。
然后最后珂能还剩下一些东西分不了组(或者没有),这样它们就一定会没有某个数。我们把每个组的最后一个数取出来,然后在来一个最后剩余中没有的数,组成一个序列,肯定不是原序列的子序列。并且这个序列的长度是组数 + 1 +1 +1。
证明这个序列不是原序列的子序列:
设有 g g g个组,第 i i i个组跨的区间是 [ l i , r i ] [l_i,r_i] [li,ri],原序列叫 a a a,我们构造出来的序列叫 b b b。
b b b的前 g g g个数在前 g g g个组中,上已证,只有一次出现。那么, b b b的前 g g g个数就能 跳 过 前 k 个 组 \color{#ff0000}跳过前k个组 跳过前k个组,然后最后选的那个数是剩下的数没有的,就把剩下的数也跳过了,而且和前面那个“跳过”还不太一样,前面是找到了跳过,这个是没找到跳过。那么就找不到匹配。所以,我们选的 b b b是满足条件的。而非常显然,没有比 b b b更短的串了。所以答案就是 g + 1 g+1 g+1。
思维过程和证明不少,但是具体事项的话,我们只要 O ( n ) O(n) O(n)求出 g g g,然后把它 + 1 +1 +1就珂以了。
代码:
(顺便说一句,毒瘤hdu是多组数据的!!!而且还没说!!!)
(这里的代码是
3
3
3个
O
J
OJ
OJ都能
A
C
AC
AC的代码)
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 100100
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
int n,k;
int a[N];
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
for(int i=1;i<=n;++i)
{
R1(a[i]);
}
}
int cnt[N/5];//常数小一点
void Soviet()
{
FK(cnt);
int have=0;
int group=0;
for(int i=1;i<=n;++i)
{
if (cnt[a[i]]==0) ++have;
++cnt[a[i]];
if (have==k)//刚到满足的时候,就要存储下这个组,然后初始化去找下一个
{
++group;
have=0;
FK(cnt);
}
}
printf("%d\n",group+1);
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
while(scanf("%d%d",&n,&k)>0)
{
Input();
Soviet();
}
}
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}