题目大意:定义k-bag为k的随机排列的循环(比如:1 2 3 3 2 1 2 1 3 3 1 2这种),定义part-k-bag为k-bag的一个连续子序列,给你个序列,长度为n及其对应的k,判断是不是part-k-bag。
思路:首先序列肯定满足前面有一段不完全排列(当然也有可能是全的),中间连续好几段完全的排列,后面又一段不全的,所以就可以从开头的这段不完全的开始一个个作为起点试,如果发现走得通就输出YES,如果开头每个都试完了还是走不通就输出NO。之后就可以考虑怎么来判断中间那段是不是连续的好几段完全的排列:定义个数组c,c[i]表示原序列第i个数之前和他相同的最近的数的位置,然后咱们对于一段原序列[L,R],他所有对应的c[i]都要小于L才可以,所以就变成了个RMQ问题,用ST表或者线段树啥的做一下就行了。
因为k范围太大,所以需要离散化一下。
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int n,k;
int cnt;
int a[maxn],b[maxn],c[maxn];
int st,ed;
int rec[maxn];
struct node {
int mx;
int left,right;
}tree[maxn<<2];
int build(int root,int left,int right)
{
tree[root].left=left,tree[root].right=right;
if(tree[root].left==tree[root].right){
tree[root].mx=c[tree[root].left];
return tree[root].mx;
}
int mid=(left+right)>>1;
int x1=0,x2=0;
x1=build(root<<1,left,mid);
x2=build(root<<1|1,mid+1,right);
return tree[root].mx=max(x1,x2);
}
int search(int root,int left,int right)
{
if(left<=tree[root].left&&tree[root].right<=right) return tree[root].mx;
int x1=0,x2=0;
if(tree[root<<1].right>=left) x1=search(root<<1,left,right);
if(tree[root<<1|1].left<=right) x2=search(root<<1|1,left,right);
return max(x1,x2);
}
void solve()
{
int t;scanf("%d",&t);
while(t--){
memset(tree,0,sizeof(tree));
scanf("%d%d",&n,&k);
int mx=-1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
mx=max(mx,a[i]);
}
if(mx>k){
printf("NO\n");
continue;
}
sort(b+1,b+n+1);
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;
for(int i=1;i<=cnt;i++) rec[i]=-1;
for(int i=1;i<=n;i++){
c[i]=rec[a[i]];
rec[a[i]]=i;
}
build(1,1,n);
for(int i=1;i<=cnt;i++) rec[i]=-1;
int flag=1;
int delay=0;
for(int i=1;i<=k;i++){
if(delay) break;
if(rec[a[i]]!=-1) delay=1;
rec[a[i]]=i;
flag=1;
for(int j=i;j<=n;j+=k){
int L=j,R=j+k-1;
int mx=search(1,L,R);
if(mx>=L){
flag=0;
break;
}
}
if(flag) break;
}
if(flag) printf("YES\n");
else printf("NO\n");
}
}
int main()
{
solve();
}