题意:
多组询问,一个长为 n 的数列 a 和一个数 m,求在数列 a 中删掉几个数,使得从 1到 m 的所有数组成的集合是数列 a 中所有数的因数组成的集合的子集,求删数后数列 a 的极差。
思路:
考虑对 a 序列从小到大排序。可以发现,一定存在一种最优方案,使得选出的数在 a 排序后的序列中是一段连续区间,且极差最小。因为如果存在一种最优方案选取的不是连续区间,那么将这些数最左端和最右端内全部选取,仍然符合题意,极差不变,但是选择的数是连续区间。
因此将 a 排序后,考虑双指针,因此我们只需要维护一段连续区间是否符合题意。
AC代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int a[N],cnt[N];
vector<int>f[N];
set<int>s;
void solve(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
f[i].clear();
}
s.clear();
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
int x=a[i];
for(int j=1;j*j<=x;j++){
if(x%j==0){
f[i].push_back(j);
if(j*j!=x){
f[i].push_back(x/j);
}
}
}
}
for(int i=1;i<=k;i++){
s.insert(i);
cnt[i]=0;
}
int l=1,r=0,ans=1e9;
int flag=0;
while(l<=n&&r<=n){
if(flag==0){
r++;
if(r>n) break;
for(auto j:f[r]){
if(j<=k){
cnt[j]++;
if(cnt[j]==1){
s.erase(j);
}
}
}
if(s.size()==0){
ans=min(ans,a[r]-a[l]);
flag=1;
}
}
else {
if(l>n) break;
for(auto j:f[l]){
if(j<=k){
cnt[j]--;
if(cnt[j]==0){
s.insert(j);
}
}
}
if(s.size()==0){
ans=min(ans,a[r]-a[l+1]);
}
else flag=0;
l++;
}
}
if(ans==1e9) ans=-1;
cout<<ans<<"\n";
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--){
solve();
}
}