原题请看这里
当一个数列可以表示为若干个 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,所以我们需要对输入的数组离散化在进行操作,具体步骤请看代码。
#include
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");
}
}