**2020暑期牛客多校第六场K.K-Bag(思维+离散化)

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/littlegoldgold/article/details/107622720