2020牛客暑期多校第六场K- K-bag(思维)

题目这里

题意:定义k-bag就是若干个1到k的序列组成的序列,每一个1~k序列只能包含k个数,也就是不能重复,列如 1 2 3 2 3 1 3 2 1 叫做一个 3 - bag。而k-bag的一段连续子序列又叫做part k-bag。现在给定你n个数和k,问你这n个数是否是一个 part k-bag。

思路:我们首先用一个len数组来确定,从当前位置开始的不重复序列的长度是多少。这样遇到当前len[i] == k 的位置就说明是给出序列中间部分能够形成k个不重复的的序列,这样每次递增k的长度 如果一直len的长度都等于k说明是可行的。
因为给出的序列是不全的,所以除了中间完整的部分,前后都有可能还有部分需要补充才能形成一个k序列,因此我们需要把前面不圈的部分也当做起点枚举,因为我们也不知道前几个不全的能组成一个完整的k序列,所以枚举起点到和开头位置第一次重复的点的位置,因为再往后面走,前面的就不可能形成k序列了,而当一个点的位置加上len大于n时,就说明从这个点开始后面都没有重复的点,那这个部分也可以补全为一个k序列。

这道题k <= 1e9,如果用一个数组记录当前的数出没出现过是不显示的,所以需要离散化一下给出的a数组

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 5e5+7;
int a[MAXN],b[MAXN],len[MAXN],pre[MAXN];
int n,k;

void lisan()
{
    
    
	for(int i = 1;i <= n;i ++){
    
    
		scanf("%d",&a[i]);
		b[i] = a[i];
	}
	sort(b+1,b+1+n);
	int cnt = unique(b+1,b+1+n)-b-1;
	for(int i = 1;i <= n;i ++){
    
    
		a[i] = lower_bound(b+1,b+1+cnt,a[i])-b;
	}
}

int main()
{
    
    
	int t;
	scanf("%d",&t);
	while(t--){
    
    
		scanf("%d%d",&n,&k);
		lisan();
		int pos = 1,flag = 0;
		for(int i = 1;i <= n;i ++){
    
    
			while(!pre[a[pos]] && pos <= n)
				++pre[a[pos]],pos++;//一直没有出现过 就是一直都没有重复就往后加就行了
			--pre[a[i]];//当了前位置 把这个位置的减掉后面的相同的才可以进来
			len[i] = pos - i;
		}
		int ed = min(k,len[1]+1);//来确定起点有几种可能性 要么与开头第一个重复,要么就是k 超过k也没有意义了
		for(int i = 1;i <= ed;i ++){
    
    
			int f = 1;
			for(int j = i;j <= n;j += k){
    
    
				if(j + len[j] > n) continue;
				else if(len[j] != k){
    
    
					f = 0;//这个起点不可行
					break;
				}
			}
			if(f == 1){
    
    //找到了一个可行的起点 就完成了
				flag = 1;
				break;
			}
		}
		if(flag) puts("YES");
		else puts("NO");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45672411/article/details/107663552