Codeforces Round 845 (Div. 2) and ByteRace 2023(C. Quiz Master)

传送门

Codeforces Round 845 (Div. 2) and ByteRace 2023(C. Quiz Master)_第1张图片

Codeforces Round 845 (Div. 2) and ByteRace 2023(C. Quiz Master)_第2张图片

 

题意:

多组询问,一个长为 n 的数列 a 和一个数 m,求在数列 a 中删掉几个数,使得从 11到 m 的所有数组成的集合是数列 a 中所有数的因数组成的集合的子集,求删数后数列 a 的极差。

思路:

考虑对 a 序列从小到大排序。可以发现,一定存在一种最优方案,使得选出的数在 a 排序后的序列中是一段连续区间,且极差最小。因为如果存在一种最优方案选取的不是连续区间,那么将这些数最左端和最右端内全部选取,仍然符合题意,极差不变,但是选择的数是连续区间。

因此将 a 排序后,考虑双指针,因此我们只需要维护一段连续区间是否符合题意。

AC代码:

#include
#define int long long
using namespace std;
const int N=1e6+10;
int a[N],cnt[N];
vectorf[N];
sets;
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<>t;
	while(t--){
		solve();
	}
} 

你可能感兴趣的:(codefoeces,思维题,算法,数据结构)