题目描述
闲的胃疼的Roundgod定义了一种叫k-bag的序列,这类序列由若干个从1~k的排列依次排列组成,例如1,2,3,2,1,3,3,2,1就是一个有效的3-bag序列。
但Roundgod对k-bag序列感到不满意,于是又定义了part-k-bag。如果一个序列是一个k-bag的连续子串,那么这个序列就叫做part-k-bag序列。
现在需要判断一个序列是否是part-k-bag序列。
输入描述
第一行输入一个数据T,表示测试数据的数目;
每个测试数据的第一行输入两个数字n,k(,
);
接下来一行输入n个整数,表示该序列,其中确保 ,每个整数在
到
的范围内。
输出描述
输出一行,若该序列是part-k-bag序列,则输出“YES”,否则输出“NO”(只需输出引号内的内容,无须输出引号)。
样例
- 输入
1
8 3
2 3 2 1 3 3 2 1 - 输出
YES
题解思路
看到这个数据范围,而且这是看数组的,首先建议离散化。
怎么说呢,这个序列可以通过放“隔板”来分割成一个个1到k的排列或部分排列,而隔板之间就是一个个区间。
我们可以先求共有多少隔板,然后移动区间,是区间的左端点在0到k之间。然后左端点加一右端点减一并求前缀和,最后找一下有没有数恰好等于区间数。
当然,题中所给数据范围并没有说明序列中的数一定小于k,所以要特判,而且显然这种情况是不合法的。
官方题解:
参考代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=500500;
int n,k,a[MAXN],b[MAXN],pre[MAXN],sum[MAXN];
void disc()//离散化
{
sort(b+1,b+1+n); int cnt=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
bool flag=1;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",a+i),b[i]=a[i];
if(a[i]>k) flag=0;//特判
}
if(!flag){printf("NO\n");continue;}
if(k>n) disc();
memset(pre,0,sizeof(pre));
memset(sum,0,sizeof(sum));
int cnt=0,m=min(n+1,k);
for(int i=1;i<=n;i++)
{
if(pre[a[i]]&&pre[a[i]]>i-k)
{
cnt++;//调整
sum[pre[a[i]]%k]++;
sum[i%k]--;
if(pre[a[i]]%k>=i%k)
{
sum[m]--;
sum[0]++;
}
}
pre[a[i]]=i;
}
flag=0;
for(int i=0;i<m;i++)
{
if(i)sum[i]+=sum[i-1];
if(sum[i]==cnt)//查找
{
flag=1;
break;
}
}
if(flag)printf("YES\n");
else printf("NO\n");
}
}
京公网安备 11010502036488号