题意
K-bag是一个正整数序列,由若干个111~kkk的排列排成,其连续子序列被称为部分K-bag。
给出一个序列和正整数kkk,判断该序列是不是部分K-bag。
(n≤5e5,k≤1e9)(n\leq5e5,k\leq1e9)(n≤5e5,k≤1e9)
题解
题目给的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)(i−k+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[i−k]) 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[i−k]: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");
}
}