题目链接:https://ac.nowcoder.com/acm/contest/5671/K
题意:k-bag序列指由一个或多个1-k排列组成的序列
part-k-bag序列指k-bag的一个连续子序列
解题思路:
参考
每两个相同元素不可能同时在同一个长度为k的序列中
用len[i]表示从i开始的最长的序列,即从i开始无重复数的序列长度
pre[i]表示当前前面有多少个数字i
具体代码解释可以看注释
PS:因为k最大可以为1e9, n最大可以为5e5,所以使用离散化把数组压缩到5e5以内,防止数组过大
unique函数只能用在排过序的数组中,且返回的是不重复序列的最后一个元素的下一个位置
#include<iostream>
#include<cstdio>
#include<string.h>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
#define ll long long
int t;
int n,k;
int a[510000];
int b[510000];
int len[510000];
int pre[510000];
int main(){
cin>>t;
while(t--){
cin>>n>>k;
bool ok=true;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
if(a[i]>k||a[i]<0)
ok=false;
}
if(!ok){
cout<<"NO"<<endl;
continue;
}
sort(b+1,b+n+1);
int cnt=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
int p=1; //记录当前记录到的位置
//处理len[i]
for(int i=1;i<=n;i++){
while(!pre[a[p]]&&p<=n)
pre[a[p]]++,p++;
--pre[a[i]]; //去掉当前位置处的数
len[i]=p-i; //记录从i开始的最长长度(无重复序列)
}
int sc=0;
//st表示起点位置,1 - st之间相当于前端部分
//因为是前端部分,所以st只能是 1 - min(k,len[1]+1) ,特别注意是len[1]+1
//从st开始是每k个长度单位都是一个1-k的排列
//i+len[i]>=n+1时 i - i+len[i]-1相当于后端部分
for(int st=1;st<=min(k,len[1]+1);st++){
bool flag=true;
for(int i=st;i<=n;i+=k){
if(i+len[i]>=n+1)
continue;
else if(len[i]!=k){
flag=false;
break;
}
}
if(flag==true){
sc=1;
break;
}
}
printf(sc?"YES\n":"NO\n");
}
return 0;
}