题目大意: 定义名为K-bag的一类数组:由几个k的全排列头尾链接组成的数组。
如一个3-bag: 【1 2 3 3 2 1 2 1 3 】 是由【1 2 3】【3 2 1】【2 1 3】连接而成的数组。
给你一个长度为n的数组和一个数k,(1<=n<=2e6,1<=k<=1e9)问其是否是一个K-bag的子数组(连续)。
【2 3 1 2】=>【2 3 1 || 2 (1)(3)】=> YES
解:
若数组【a1,a2,a3,.....,an-1,an】为K-bag子串。则在数组前后各自添上一些数后,数组变为K-bag。
即【b1,b2,...,a1,a2...,an,c1,c2...,cx]为一个k-bag。定义:k-bag的分割点————k-bag中的俩个连续的全排列的分开的位子.
如【1 2 (3) 3 2 (1) 2 1 (3)】中加括号的数即为此3-bag的分割点。
注意到分割点的前k个数合为一全排列。
则数组中某数ai能否作为分割点,取决于其前k个数是不是一个全排列,即[ai-k+1,ai]中每个数只出现1次。
那么,若数组中间隔为k的某一数列(【ai,ai+k,ai+2k,...】),均可作为分割点,则数组合法。
分割点的判断:
定义一个bool vis【maxn】数组。用于维护前缀数组的信息。
vis【i】表示i作为分割点后,【a1,a2....,ai】是否合法。
合法取决于,ai的前k个数中,没有一个数出现了两次,且vis【i-k】==true(即【a1,...,ai-k】也合法)
然后前后各扫一遍数组,统计区间每个长度为k的区间是否有某个数出现次数大于1即可。
#include<bits/stdc++.h> using namespace std; const int maxn=2e6+10; const int inf=1e8; void in(int &x){ int y=1;char c=getchar();x=0; while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();} while(c<='9'&&c>='0'){ x=(x<<1)+(x<<3)+c-'0';c=getchar();} x*=y; } void o(int x) { if (x < 0){ putchar('-'); x = -x; } if (x > 9)o(x / 10); putchar(x % 10 + '0'); } int read(){ int res=0; int y=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();} while(c<='9'&&c>='0'){res=(res<<1)+(res<<3)+c-'0';c=getchar();} return res*y; } int n,k; int a[maxn]; int cnt[maxn]; vector<int>v; int getid(int x){ return lower_bound(v.begin(),v.end(),x)-v.begin()+1; } bool vis[maxn];//vis[i]表示是否可以在此处可以作为k-bag中分割点,且vis【i-k】=true。(确立一个点作为分割点以后,则其余分割点也yi'j确立。 int sum2;//区间个数大于2的数的个数 void add(int x){//加上一个数 x=getid(x); if(cnt[x]==1){sum2++;}//x的个数从1变为2,sum2+=1; cnt[x]++; } void del(int x){//删去一个数 x=getid(x); if(cnt[x]==2){sum2--;}//2->1,sum-=1 cnt[x]--; } void getvis(){ sum2=0; for(int i=1;i<=n;i++){vis[i]=false;cnt[i]=0;}//初始化 vis[0]=true;//0前k个数未确定,必定合法 for(int i=1;i<=n;i++){ add(a[i]); if(i<=k){if(sum2==0)vis[i]=true;}//区间小于k ,只需加上 else{ del(a[i-k]);//删去一个数 if(sum2==0&&vis[i-k]){vis[i]=true;}//判断合法性 } } } bool fan(){ sum2=0; for(int i=1;i<=n;i++){cnt[i]=0;} for(int i=n;i>n-k&&i>=1;i--){ add(a[i]); if(sum2==0&&vis[i-1]){return true;}//若后缀前缀均数组合法,YES } return false; }; bool check(){ for(int i=1;i<=n;i++){ if(a[i]>k){ printf("NO\n"); return false; } } return true; } signed main(){ int T; in(T); while(T--){ in(n);in(k);v.clear(); for(int i=1;i<=n;i++){in(a[i]);v.push_back(a[i]);}//读入 if(!check()){continue;}//找找有没有数比k还大 sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());//v用于离散化 getvis();//求前缀合法性 if(fan()){printf("YES\n");}//求后缀合法性并判断 else {printf("NO\n");} } return 0; }