https://ac.nowcoder.com/acm/contest/5671/E
题解:如果序列是k-bag的连续子串的话,满足一下三种条件之一:
1.一个部分k-bag的前缀+若干个完整的k-bag+一个部分k-bag的后缀
2.一个部分k-bag的前缀+一个部分k-bag的后缀
3.一个部分k-bag
判断部分k-bag:只需要判断区间内不同的数的个数是否等于区间的长度。
这里cnt1[i]表示1~i区间内有多少不同的数,cnt2[i]表示n~i区间内有多少不同的数
判断完整的k-bag:连续序列[l,r],维护 sum 和 xor ,sum = l + ( l + 1 ) + ... + ( r - 1 ) + r ,xor = l ^ ( l + 1 ) ^ ... ^ ( r - 1 ) ^ r ,形成一个二元对 < sum , xor >,这个二元对和 [ l , r ] 是一一对应的.(这里为防止冲突,对每一个值乘以131,然后再求和,异或)
对于n>k,只能是条件1和2.我们枚举[1,k]作为第一个完整k-bag的起点,然后判断每一个长度为k的区间是否满足,根据预处理的前缀和与异或和
对于k>=n,只能是条件2和3.那么我们枚举断点判断前部分和后部分就行了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
ll a[maxn],n,k;
ll sum[maxn],Xor[maxn],S,X;
unordered_map<ll,ll> mp;
int cnt1[maxn],cnt2[maxn];
bool check(int st) {
for(int i=st;i<=n;i+=k) {
int l=i,r=i+k-1;
if(r<=n) {
if(sum[r]-sum[l-1]!=S||(Xor[r]^Xor[l-1])!=X)
return false;
} else return cnt2[l]==n-l+1;
}
return true;
}
void init() {
mp.clear();
cnt1[0]=0;
for(int i=1;i<=n;i++) {
cnt1[i]=cnt1[i-1];
if(!mp[a[i]]) cnt1[i]++;
mp[a[i]]++;
}
mp.clear();
cnt2[n+1]=0;
for(int i=n;i>=1;--i) {
cnt2[i]=cnt2[i+1];
if(!mp[a[i]]) cnt2[i]++;
mp[a[i]]++;
}
}
bool solve() {
if(k<n) {
S=X=0;
for(int i=1;i<=k;i++) S+=(i*131),X^=(i*131);
for(int i=1;i<=k;i++) {
if(cnt1[i-1]!=i-1) {
break;
}
if(check(i)) return true;
}
} else {
for(int i=2;i<=n;i++) {
if(cnt1[i-1]==i-1&&cnt2[i]==n-i+1) return true;
}
}
return false;
}
signed main() {
int test; scanf("%d",&test);
while(test--) {
scanf("%lld%lld",&n,&k);
int flag=0;
for(int i=1;i<=n;i++) {
scanf("%lld",a+i);
if(a[i]<1||a[i]>k) flag=1;
a[i]=a[i]*131;
sum[i]=sum[i-1]+a[i];
Xor[i]=Xor[i-1]^a[i];
//每一个数乘以131然后哈希,防止冲突
}
if(flag) {
puts("NO");
continue;
}
init();
if(solve()) puts("YES");
else puts("NO");
}
}