2020牛客暑期多校训练营(第六场)K.K-Bag

K.K-Bag

题目链接-K.K-Bag
在这里插入图片描述
在这里插入图片描述
题目大意
一种叫 k − b a g k-bag kbag的序列,这类序列由若干个从 1 − k 1-k 1k的排列依次排列组成,例如 1 , 2 , 3 , 2 , 1 , 3 , 3 , 2 , 1 1,2,3,2,1,3,3,2,1 1,2,3,2,1,3,3,2,1就是一个3-bag序列,现在又定义了 p a r t   k − b a g part\ k-bag part kbag,如果一个序列是一个 k − b a g k-bag kbag的连续子串,那么这个序列就叫做 p a r t   k − b a g part\ k-bag part kbag序列,请你判断一个序列是否是 p a r t   k − b a g part\ k-bag part kbag序列

解题思路
滑 动 窗 口 问 题 滑动窗口问题

  • 首先我们要特判一下该序列中是否有大于 k k k的数字,显然如果存在大于 k k k的数字是不合法的,直接输出NO
  • 然后可以通过“切割”的方式把序列分割成若干个 1 1 1 k k k的排列或部分排列;我们可以先统计区间内的不同元素的个数,如果区间内有 k k k个或 i i i个不同的数字说明该点可以用于切割,用数组f[]来记录该位置可不可以切
  • 然后我们倒序统计区间内不同元素的个数,然后判断有没有区间内不同元素个数恰好等于该区间元素个数的,则说明这个点从后面可以切割,若它从前面也可以切割(f[i]!=0),则说明存在一种方案满足该序列是一个 p a r t   k − b a g part\ k-bag part kbag
  • 具体操作见代码

附上代码

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) (x &(-x))
#define endl '\n'
using namespace std;
const int INF=0x3f3f3f3f;
const int dir[4][2]={-1,0,1,0,0,-1,0,1};
const double PI=acos(-1.0);
const double e=exp(1.0);
const double eps=1e-10;
const int M=1e9+7;
const int N=5e6+10;
typedef long long ll;
typedef pair<int,int> PII;
typedef unsigned long long ull;
int a[N],f[N];
unordered_map<int,int> mp;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);

	int t;
	cin>>t;
	while(t--){
		mp.clear();
		int n,k,ass=0,cnt=0;
		cin>>n>>k;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			if(a[i]>k) ass=1;
		}
		if(ass){
			cout<<"NO"<<endl;
			continue;
		}
		memset(f,0,sizeof f);
		f[0]=1;
		for(int i=1;i<=n;i++){
			if(i>k){
				mp[a[i-k]]--;
				if(mp[a[i-k]]==0) cnt--;
			}
			if(mp[a[i]]==0) cnt++;
			mp[a[i]]++;
			if(cnt==k||cnt==i)
				f[i]=i-k>=0?f[i-k]:1;
		}
		cnt=0;
		mp.clear();
		for(int i=n;i>=max(n-k,0ll);i--){
			if(f[i]&&cnt==n-i){
				ass=1;
				break;
			}
			if(mp[a[i]]==0) cnt++;
			mp[a[i]]++;
		}
		if(ass) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值