2020牛客暑期多校训练营(第六场) K-Bag

原题
题目描述
当一个数列可以表示为若干个1到k的排列依次组成时,这个数列被称为k-bag。例如1,2,3,2,1,3,3,2,1是一个3-bag。
如果一个序列是一个k-bag的连续子串,则其称为part-k-bag。
求一个长度为n的序列是否是一个part-k-bag。
第一行包含一个整数T(1≤T≤20),表示测试用例的数量。
然后是T个样例。每个测试案例的第一行包含两个整数n,k (1≤n≤5⋅105,1≤k≤109)。
每个测试案例的第二行包含n个整数表示序列。保证 n≤2⋅106,序列的值在
1到109之间。
如果一个序列是部分k-bag序列,则打印“是”,否则打印“否”。
样例
输入

1
8 3
2 3 2 1 3 3 2 1

输出

YES

思路
这是官方题解,大家可以先看看。
在这里插入图片描述
本题可以看成一个放隔板的问题,每个相同数字间都要放一个隔板。我们把隔板看成一个个区间,若有两个区间相互没有交集,则表示它不是part-k-bag。
因为序列的值在 1 1 ~ 1 e 9 1e9 之间,直接开数组肯定存不下,所以要用离散化。
我们可以先求出每个节点上放有多少块隔板。先把区间往前移动xk个单位,使区间的左端点在0到k之间。然后在左端点加一,右端点减一,最后再求一下前缀和即可。若有一处的隔板数量是离散化的最大值,则表示所有的隔板都可以放在上面,即任意的两个区间都有交集。
特殊情况:若序列中某个元素的值大于k,则这个序列肯定是不合法的。
区间移动的时候如果直接取模,则会出现有两种情况,具体可以看看代码。
代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
int a[maxn],b[maxn],c[maxn],d[maxn],cnt,sum,t,n,k;
int main()
{
    for(scanf("%d",&t);t--;)
    {
        bool flag=1;d[0]=0;cnt=0;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]>k)flag=0;
            b[i]=a[i];c[i]=d[i]=0;
        }
        if(!flag){puts("NO");continue;}
        sort(b+1,b+n+1);sum=unique(b+1,b+n+1)-b-1;//离散化
        for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+sum+1,a[i])-b;
        for(int i=1;i<=n;i++)//移动区间并标记
        {
            if(c[a[i]]&&c[a[i]]>i-k)
            {
			    d[c[a[i]]%k]++,cnt++;
            	if(c[a[i]]%k<i%k) d[i%k]--;
            	//移动后左端点在右端点的左边
            	else d[min(k,n+1)]--,d[0]++,d[i%k]--;
            	//移动后左端点在右端点的右边
            }
            c[a[i]]=i;
        }
        if(d[0]==cnt){printf("YES\n");continue;}
        for(int i=1;i<=min(n,k-1);i++)//求前缀和
        {
        	d[i]+=d[i-1];
        	if(d[i]==cnt){flag=0;break;}
        }
        if(!flag)puts("YES");
        else puts("NO");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/bbbll123/article/details/107633642