K-Bag
题目描述
当一个数列可以表示为若干个
1
1
1到
k
k
k的排列依次组成时,这个数列被称为
k
−
b
a
g
k-bag
k−bag。例如
1
,
2
,
3
,
2
,
1
,
3
,
3
,
2
,
1
1,2,3,2,1,3,3,2,1
1,2,3,2,1,3,3,2,1是一个
3
−
b
a
g
3-bag
3−bag。
如果一个序列是一个
k
−
b
a
g
k-bag
k−bag的连续子串,则其称为
p
a
r
t
−
k
−
b
a
g
part-k-bag
part−k−bag。
求一个长度为
n
n
n的序列是否是一个
p
a
r
t
−
k
−
b
a
g
part-k-bag
part−k−bag。
输入描述:
第一行包含一个整数
T
(
1
≤
T
≤
20
)
T(1≤T≤20)
T(1≤T≤20),表示测试用例的数量。
然后是
T
T
T个样例。每个测试案例的第一行包含两个整数
n
,
k
(
1
≤
n
≤
5
⋅
1
0
5
,
1
≤
k
≤
1
0
9
)
n,k(1≤n≤5⋅10^5,1≤k≤10^9)
n,k(1≤n≤5⋅105,1≤k≤109)。
每个测试案例的第二行包含n个整数表示序列。保证
∑
n
≤
2
⋅
1
0
6
∑n≤2⋅10^6
∑n≤2⋅106,序列的值在
1
1
1到
1
0
9
10 ^ 9
109之间。
输出描述:
如果一个序列是部分 k − b a g k-bag k−bag序列,则打印 “ Y E S ” “YES” “YES”,否则打印 “ N O ” “NO” “NO”。
样例输入:
1
8 3
2 3 2 1 3 3 2 1
样例输出:
YES
思路:
这题我调了8个小时!没错,你没听错,就是8个!
刚开始,我一看到这道题目就大叫一声:这不是签到题嘛!然后就兴冲冲的
W
A
WA
WA了一发
.
.
.
.
.
.
......
......
再一看,原来没有这么简单,又打了一会儿,又兴冲冲的
W
A
WA
WA了一发
.
.
.
.
.
.
......
......
然后就
w
h
i
l
e
(
t
r
u
e
)
while(true)
while(true)
p
r
i
n
t
f
(
"
W
A
"
)
;
printf("WA");
printf("WA");
(
(
(想
T
T
T都不给
T
)
T)
T)
咳咳,回归正题
我们首先可以想到的解法:
f
o
r
for
for寻找分割点,然后判断分割点是否合法,但显然会超时
随后,我们可以想到:只要在每两个相同的数之间的区间内有分割点就可以了,由于分割点是每k个数一个,所以我们可以把所有的区间都向前移
a
∗
k
a*k
a∗k位,如果都有交集,说明是合法的。
但很快就又
W
A
WA
WA了,为什么呢?
这就是反例。
我们就应该维护两端的区间而不是只考虑一端的区间,这就需要用到前缀和来解决。
将一个区间的左端点
+
1
+1
+1,右端点
−
1
-1
−1,再统计一下前缀和就可以发现:前缀和数组中的数就是当前位置覆盖的区间数,最后只要找有没有数值等于区间数就可以了
由于k的取值有
1
e
9
1e9
1e9,所以我们需要对输入的数组离散化在进行操作,具体步骤请看代码。
AC Code:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e6+5;
int n,k,t,fl,lsh,lap[MAXN],qian[MAXN],cnt;
int a[MAXN],b[MAXN];
int main()
{
scanf("%d",&t);
while(t--)
{
fl=1,cnt=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
if(a[i]>k) fl=0;//如果输入的数大于k,那么就肯定不行
}
if(!fl)
{
puts("NO");
continue;
}
sort(b+1,b+1+n);
int QAQ=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+1+QAQ,a[i])-b;
//离散化
memset(lap,0,sizeof(lap));
memset(qian,0,sizeof(qian));//前缀和
for(int i=1;i<=n;i++)
{
if(lap[a[i]]&&lap[a[i]]>i-k)//是否有相同的数出现
{
cnt++;//区间数
qian[lap[a[i]]%k]++;
qian[i%k]--;
if(lap[a[i]]%k>=i%k)
{
qian[0]++;
qian[min(k,n+1)]--;
}
}
lap[a[i]]=i;
}
for(int i=0;i<=min(n,k-1);i++)
{
if(i) qian[i]+=qian[i-1];
if(qian[i]==cnt)
{
fl=0;
break;
}//如果有符合的值就跳出
}
if(fl) puts("NO");
else puts("YES");
}
}