题目链接:https://ac.nowcoder.com/acm/contest/5671/K
题目大意:
k-bag的定义是指:
一个序列恰好有若干个 长度为k的全排列构成
给出一个序列,询问当前序列是否是一个k-bag的连续子序列
题目思路:
这种题基本就是考虑最终状态
考虑最终状态:绝对是在两边加了几个使其变成了k-bag
在两边加可以转换为只在左边加,在左边加又可以转换为把前x个隔离出去
比如 3-bag 2 3 2 3 1 1 在左边+1个,就相当于保留前2个,后面保留了1个
所以说题目就变成了 中间的一部分是否是k-bag
所以思路就变成了枚举所有状态
关键是如何做到整体的On check是否是k-bag
可以考虑每次check不需要重新定义 只需要check n/m个点即可
1 2 3 3 2 1
check这段序列只需要check 第三个位置向前是否可行,第6个位置向前是否可行
所以说思路也就出来了
受限预处理数组f_i,fi表示第i个位置向前是否是全排列
之后checkm次,每次n/m的复杂度,总体就是O(n)的check
预处理f数组就是典型的滑动窗口问题
Note几个坑:
1.当有大于m的数时,NO!
2.本身就是k-bag,NO!
3.不要忘记check保留的数!!保留的数有重复的必然不可以!
4.map处理超时了,手写了哈希散列~
Code:
/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pp;
const ll INF=1e17;
const int Maxn=2e7+10;
const int maxn =1e6+10;
const int mod= 1e9+7;
const int Mod = 1e6+7;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll a[maxn];
ll ncnt = 0;
struct Hash{
int e,next,w;
}H[maxn];
int head[maxn];
void Insert(ll x,ll wt){
ll temp=x%Mod;
for(int i=head[temp];~i;i=H[i].next){
if(H[i].e==x){
H[i].w += wt;
return;
}
}
H[ncnt]=Hash{x,head[temp],wt};
head[temp]=ncnt++;
}
ll Find(ll x){
ll temp=x%Mod;
for(int i=head[temp];~i;i = H[i].next){
if(H[i].e==x) return H[i].w;
}
return 0;
}
ll cnt = 0;
void del(ll x){
Insert(x,-1);
if(Find(x)==0) cnt--;
}
void add(int x){
Insert(x,1);
if(Find(x) == 1) cnt++;
}
int f[maxn];
int pre[maxn],cur[maxn];
int judge(int x){
int k = x+m;
for(;k<=n;k+=m) if(!f[k]) return 0;
if(!pre[x]) return 0;
if(!cur[k-m+1]) return 0;
return 1;
}
int work(){
if(n%m!=0) return 0;
for(int i=m;i<=n;i+=m) if(!f[i]) return 0;
return 1;
}
int main()
{
int T;scanf("%d",&T);
while(T--){
read(n);read(m);
cnt = 0;
for(int i=1;i<=n;i++) f[i] = pre[i] = cur[i] = 0;
for(int i=1;i<=n;i++) read(a[i]);
///1
int ff = 0;
for(int i=1;i<=n;i++)
if(a[i]>m) ff = 1;
if(ff){
printf("NO\n");
continue;
}
///1
///2
ncnt = 0;
memset(head,-1,sizeof(head));
for(int i=1;i<=min(n,m);i++) add(a[i]);
if(cnt == m) f[m] = 1;
for(int i=m+1;i<=n;i++){
del(a[i-m]);
add(a[i]);
if(cnt == m) f[i] = 1;
}
///2
///3
ff = work();
if(ff){
printf("NO\n");
continue;
}
///3
///4
pre[0] = 1;
ncnt = 0;
memset(head,-1,sizeof(head));
for(int i=1;i<=min(m-1,n);i++){
if(Find(a[i])) pre[i] = 0;
else pre[i] = pre[i-1];
Insert(a[i],1);
}
cur[n+1] = 1;
ncnt = 0;
memset(head,-1,sizeof(head));
for(int i=n;i>=max(n-m+1,0ll);i--){
if(Find(a[i])) cur[i] = 0;
else cur[i] = cur[i+1];
Insert(a[i],1);
}
///4
///5
int res = 0;
for(int i=0;i<=min(m-1,n);i++){
int flag = judge(i);
if(flag){
res = 1;
break;
}
}
if(res) printf("YES\n");
else printf("NO\n");
///5
}
return 0;
}
/**
1000
**/