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

该博客主要讨论了K-Bag的概念,即由1到k的排列组成的数列,并介绍了如何判断一个序列是否是part-K-Bag。博主分享了他们在解决这个问题时遇到的挑战,从最初的误判到最终使用前缀和与区间覆盖策略解决问题的过程。文章提供了样例输入、输出,思路解析以及通过测试的代码实现。
摘要由CSDN通过智能技术生成

K-Bag

原题请看这里

题目描述

当一个数列可以表示为若干个 1 1 1 k k k的排列依次组成时,这个数列被称为 k − b a g k-bag kbag。例如 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 3bag
如果一个序列是一个 k − b a g k-bag kbag的连续子串,则其称为 p a r t − k − b a g part-k-bag partkbag
求一个长度为 n n n的序列是否是一个 p a r t − k − b a g part-k-bag partkbag

输入描述:

第一行包含一个整数 T ( 1 ≤ T ≤ 20 ) T(1≤T≤20) T(1T20),表示测试用例的数量。
然后是 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,k1n5105,1k109
每个测试案例的第二行包含n个整数表示序列。保证 ∑ n ≤ 2 ⋅ 1 0 6 ∑n≤2⋅10^6 n2106,序列的值在 1 1 1 1 0 9 10 ^ 9 109之间。

输出描述:

如果一个序列是部分 k − b a g k-bag kbag序列,则打印 “ 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 ak位,如果都有交集,说明是合法的。
但很快就又 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");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值