牛客多校第六场 K-K-Bag(离散化、枚举)

目录

  • 题意
  • 解题思路
  • 代码

题意

  • 链接:K-Bag
  • 定义一种k-bag序列为由多个k的全排列组成的数列,而part-k-bag序列是k-bag序列的连续子序列
    现给出n、k,以及一个长为n的序列判断该序列是否为part-k-bag序列
  • 范围: 1<=k<=109 1<=n<=5*105

解题思路

  • 若序列 a[i]<1 || a[i]>k 则肯定不符合
  • 因为k可达109需将其离散化下标降至5*105
    unique是去重函数,对已经排序的数组去重,并返回去重后的长度的地址,减去数组名就是去重后的长度了。
  • 用len[i]表示以a[i]开始可以构成数字不重复的序列的最长长度
    用pre[x]表示x是否出现过,在维护len[i]时,只要1-k中有重复了就要截取了
    牛客多校第六场 K-K-Bag(离散化、枚举)_第1张图片
    例如:
    在这里插入图片描述
  • part-k-bag序列 = 【k的全排列的一部分】+ 【k的全排列+……+k的全排列】+ 【k的全排列的一部分】
    所以我们只要找到第一个完整的k的全排列的起点,然后再判断该起点的后k个是不是不一样的如果是就再跳k个再次判断,否则就不是起点,注意当j+len[j]>n时说明以及是的部分是符合并且要停止再跳
    第一个元素a[1]与元素a[len[i]+1]之间(包括端点)都是可能的起点,我们枚举判断每个起点是否符合,若都不符合则输出NO

代码

#include
#include
#include
using namespace std;
const int maxn=5e5+5;
int T,n,k,a[maxn],b[maxn],pre[maxn],len[maxn];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		memset(pre,0,sizeof(pre));
		scanf("%d%d",&n,&k);
		int flag=1;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			b[i]=a[i];
			if(a[i]<1||a[i]>k)flag=0;
		}
		if(!flag)
		{
			printf("NO\n");
			continue;
		}
		if(k>n)//若k太大需离散化
		{
			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 now=1;
		for(int i=1;i<=n;i++)
		{
			while(!pre[a[now]]&&now<=n)pre[a[now++]]++;
			pre[a[i]]--;len[i]=now-i;
		}
		int cnt=min(k,len[1]+1),f1=0;
		for(int i=1;i<=cnt;i++)//枚举可能的起点
		{
			int f2=1;
			for(int j=i;j<=n;j+=k)
			{
				if(j+len[j]>n)continue;
				if(len[j]!=k)
				{
					f2=0;break;
				}
			}
			if(f2)
			{
				f1=1;break;
			}
		}
		if(f1)printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

你可能感兴趣的:(2020牛客多校赛,#,7.27第六场)