【Nowcoder】2020牛客暑期多校训练营(第六场)K- K-Bag | 思维枚举、哈希散列

题目链接: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 
#define debug(x) cout<<#x<<":"< 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

**/

 

你可能感兴趣的:(思维锻炼)