题目链接:https://ac.nowcoder.com/acm/contest/5671/K
题意
定义k-bag为1~k的若干个全排列组成的序列,part-k-bag为k-bag的连续子序列。
现在给你一个长度为n的序列,判断其是否为part-k-bag。
思路
part-k-bag的特点是中间是若干个完整的1~k全排列,左边和右边可能存在长度小于k的全排列。
首先,求len[i],表示以i为终点,i之前(包括i)最多有多少个不相同的数字。 用类似于求滑动窗口的方法求len[i]。
然后,枚举最后一个完整k-bag的终点,从j=i开始,每次向前移动len[j]长度,如果当j>=k时len[j]!=k,或者当j<k时len[j]!=j,那么以 i 为终点的方案就不可行。找到任意一个可行方案即可结束。
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
deque<int>q;
int T,n,k,a[N];
unordered_map<int,int>vis;
int len[N];
bool solve()
{
vis.clear();
while(!q.empty())q.pop_back();
for(int i=1;i<=n;i++)
{
if(a[i]>k)return 0; // 特判
q.push_back(a[i]);
if(!vis[a[i]])
{
vis[a[i]]=1;
len[i]=q.size();
}
else
{
while(!q.empty()&&vis[a[i]])
{
vis[q.front()]=0;
q.pop_front();
}
vis[a[i]]=1;
len[i]=q.size();
}
} // get every len[i]
int ex=max(n-k+1,n-len[n]);
for(int i=n;i>=ex;i--) // 枚举最后一个完整k-bag的终点
{
bool flag=1;
for(int j=i;j>0;j-=len[j])
if(len[j]!=k&&j>=k||len[j]!=j&&j<k){
flag=0;break;}
if(flag)return 1; // 找到一个可行的划分方案
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);
cin>>T;
while(T--)
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
int ans=solve();
if(ans)printf("YES\n");
else printf("NO\n");
}
return 0;
}
/*
100
6 5
1 2 1 2 1 2
NO
5 100
1 2 3 1 2
YES
len:1 2 3 3 3
5 100
2 2 2 2 2
NO
len:1 1 1 1 1
5 2
1 2 3 1 2
NO
4 2
1 1 2 2
YES
5 3
1 2 3 1 2
YES
5 3
1 2 2 2 1
NO
3 3
1 2 3
YES
4 3
1 2 2 1
YES
3 3
2 1 2
YES
9 3
3 2 1 3 3 1 2 2 1
YES
6 3
3 2 1 1 2 3
YES
5 3
1 3 2 2 1
YES
5 3
1 3 2 3 1
YES
13 4
3 1 2 4 1 2 3 4 2 1 3 2 3
YES
7 3
1 2 1 2 3 3 2
YES
2 3
1 2
YES
*/