题意
K-bag是一个正整数序列,由若干个
1
1
1~
k
k
k的排列排成,其连续子序列被称为部分K-bag。
给出一个序列和正整数
k
k
k,判断该序列是不是部分K-bag。
(
n
≤
5
e
5
,
k
≤
1
e
9
)
(n\leq5e5,k\leq1e9)
(n≤5e5,k≤1e9)
题解
题目给的
k
k
k很大,所以在
k
k
k上做文章有点难。
考虑直接扫一遍,用
O
(
n
)
O(n)
O(n)做法或
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)做法。
一开始若有数字大于
k
k
k直接输出。
设数组
f
[
i
]
f[i]
f[i],记录序列
1
1
1~
i
i
i是否可以为一个部分K-bag。
为维护
f
[
i
]
f[i]
f[i],我们需要定义一个
t
o
t
tot
tot,记录
(
i
−
k
+
1
)
(i-k+1)
(i−k+1)~
i
i
i这一段上,一共有几种不同数字。
那么我们有需要一个哈希表来统计这一段上数字的出现数组,实现方式可以用
u
n
o
r
d
e
r
e
d
_
m
a
p
unordered\_map
unordered_map(因为
m
a
p
map
map会超时)或离散化使数字均小于
5
e
5
5e5
5e5然后直接用数组哈希。
若序列用
a
[
i
]
a[i]
a[i]表示,
那么就有
i
f
(
i
>
n
&
&
!
−
−
h
a
s
h
[
a
[
i
−
k
]
)
t
o
t
−
−
;
if(i>n\&\&!--hash[a[i-k])\ tot--;
if(i>n&&!−−hash[a[i−k]) tot−−;
i
f
(
h
a
s
h
[
a
[
i
]
]
=
=
0
)
t
o
t
+
+
;
if(hash[a[i]]==0)\ tot++;
if(hash[a[i]]==0) tot++;
当
i
<
k
i<k
i<k时,
f
[
i
]
=
t
o
t
=
=
i
?
1
:
0
;
f[i]=tot==i?1:0;
f[i]=tot==i?1:0;
当
i
>
=
k
i>=k
i>=k时,
f
[
i
]
=
t
o
t
=
=
k
?
f
[
i
−
k
]
:
0
;
(
f
[
0
]
=
1
)
f[i]=tot==k?f[i-k]:0;(f[0]=1)
f[i]=tot==k?f[i−k]:0;(f[0]=1)
最后从后往前查找最后
k
k
k个数字,看最后这一段能否符合以及此处
f
f
f值是否为
1
1
1。找到一个符合条件的即为
Y
E
S
YES
YES,否则为
N
O
NO
NO。
#include<bits/stdc++.h>
int a[500010],f[500010],id[500010],tong[500010];
int main(){
int T; scanf("%d",&T);
while(T--){
memset(tong,0,sizeof(tong)); memset(f,0,sizeof(f)); f[0]=1;
int n,k,tag=0,tot=0,ans=0; scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",a+i),tag=a[i]>k?1:0,id[i]=a[i];
std::sort(id+1,id+n+1);
int len=std::unique(id+1,id+n+1)-id-1;
for(int i=1;i<=n;i++) a[i]=std::lower_bound(id+1,id+len+1,a[i])-id;
if(tag) {printf("NO\n");continue;}
for(int i=1;i<=n;i++){
if(i>k) if(!--tong[a[i-k]]) tot--;
if(!tong[a[i]]) tot++;
tong[a[i]]++;
if(i>=k&&tot==k) f[i]=f[i-k];
if(i<k&&tot==i) f[i]=1;
}
tot=0; memset(tong,0,sizeof(tong));
for(int i=n;i>=std::max(n-k,0);i--){
if(f[i]&&tot==n-i) {ans=1;break;}
if(!tong[a[i]]) tot++;
tong[a[i]]++;
}
if(ans) printf("YES\n"); else printf("NO\n");
}
}