【解题报告】2020牛客暑期多校集训营第六场K题——RMQ+离散化

题目大意:定义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();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值