牛客多校第6场K-K-bag

本文介绍了一种算法,用于判断一个给定的正整数序列是否为部分K-bag,即是否能由若干个1到k的排列组成。通过一次遍历序列并使用哈希表统计特定区间内的不同数字数量,该算法能在O(n)或O(nlogn)的时间复杂度内完成任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

K-bag是一个正整数序列,由若干个111~kkk的排列排成,其连续子序列被称为部分K-bag。
给出一个序列和正整数kkk,判断该序列是不是部分K-bag。
(n≤5e5,k≤1e9)(n\leq5e5,k\leq1e9)(n5e5,k1e9)

题解

题目给的kkk很大,所以在kkk上做文章有点难。
考虑直接扫一遍,用O(n)O(n)O(n)做法或O(nlogn)O(nlogn)O(nlogn)做法。
一开始若有数字大于kkk直接输出。
设数组f[i]f[i]f[i],记录序列111~iii是否可以为一个部分K-bag。
为维护f[i]f[i]f[i],我们需要定义一个tottottot,记录(i−k+1)(i-k+1)(ik+1)~iii这一段上,一共有几种不同数字。
那么我们有需要一个哈希表来统计这一段上数字的出现数组,实现方式可以用unordered_mapunordered\_mapunordered_map(因为mapmapmap会超时)或离散化使数字均小于5e55e55e5然后直接用数组哈希。
若序列用a[i]a[i]a[i]表示,
那么就有if(i>n&&!−−hash[a[i−k]) tot−−;if(i>n\&\&!--hash[a[i-k])\ tot--;if(i>n&&!hash[a[ik]) tot;if(hash[a[i]]==0) tot++;if(hash[a[i]]==0)\ tot++;if(hash[a[i]]==0) tot++;
i<ki<ki<k时,f[i]=tot==i?1:0;f[i]=tot==i?1:0;f[i]=tot==i?1:0;
i>=ki>=ki>=k时,f[i]=tot==k?f[i−k]:0;(f[0]=1)f[i]=tot==k?f[i-k]:0;(f[0]=1)f[i]=tot==k?f[ik]:0;(f[0]=1)
最后从后往前查找最后kkk个数字,看最后这一段能否符合以及此处fff值是否为111。找到一个符合条件的即为YESYESYES,否则为NONONO

#include<bits/stdc++.h>
int a[500010],f[500010],id[500010],tong[500010];
int main(){
    int T; scanf("%d",&T);
    while(T--){
        memset(tong,0,sizeof(tong)); memset(f,0,sizeof(f)); f[0]=1;
        int n,k,tag=0,tot=0,ans=0; scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d",a+i),tag=a[i]>k?1:0,id[i]=a[i];
        std::sort(id+1,id+n+1);
        int len=std::unique(id+1,id+n+1)-id-1;
        for(int i=1;i<=n;i++) a[i]=std::lower_bound(id+1,id+len+1,a[i])-id;
        if(tag) {printf("NO\n");continue;}
        for(int i=1;i<=n;i++){
            if(i>k) if(!--tong[a[i-k]]) tot--;
            if(!tong[a[i]]) tot++;
            tong[a[i]]++;
            if(i>=k&&tot==k) f[i]=f[i-k];
            if(i<k&&tot==i) f[i]=1;
        }
        tot=0; memset(tong,0,sizeof(tong));
        for(int i=n;i>=std::max(n-k,0);i--){
            if(f[i]&&tot==n-i) {ans=1;break;}
            if(!tong[a[i]]) tot++;
            tong[a[i]]++;
        }
        if(ans) printf("YES\n"); else printf("NO\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值