题目链接: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
*/