题目描述

闲的胃疼的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");
    }
}