CF804 E-Three Days Grace

这篇博客探讨了一个数组处理的问题,即如何通过操作减少数组中元素的最大值与最小值之差(极差)。文章介绍了动态规划的方法,从小到大枚举最大因子,利用单调队列优化更新过程,最终求出最小极差。同时,提供了C++代码实现,展示了解决这类问题的思路和技巧。

CF804 E- Three Days Grace

Problem Statement

TTT组询问, 每组询问给一个数组AiA_iAi和两个整数长度NNN及其值域[1,M][1,M][1,M].

你可以进行如下操作

  • 选择一个数AiA_iAi若满足Ai=p⋅q(p,q≠1)A_i=p\cdot q(p,q\neq 1)Ai=pq(p,q=1)则可以删去AiA_iAip,qp,qp,q加入数组.
  • 你可以无数次地进行上述操作

问数组中的极差最小是多少, 即max⁡(Ai)−min⁡(Ai)\max(A_i)-\min(A_i)max(Ai)min(Ai)在若干次操作后的最小值.

Solution

假设当前最大因子为KKK,我们考虑从小到大枚举最大的因子KKK.

∀i,Ai≤K\forall i,A_i\leq Ki,AiK时, 我们需要让满足条件的AiA_iAi最大.

我们不妨设fif_ifi表示在最大因子不超过KKK的条件下, 数iii的可被分解为的最小因子的最大值为fif_ifi(即6=2×36=2\times 36=2×3则当K=3K=3K=3f6=2f_6=2f6=2).

  • 性质1: 显然有fi≤Kf_i\leq KfiK.
  • 性质2: 由于操作是将Ai=p⋅q(p,q≠1)A_i=p\cdot q(p,q\neq 1)Ai=pq(p,q=1), 显然进行一次操作后AiA_iAi会被分解称为两个更小的数, 即满足p,q≤Aip,q\leq A_ip,qAi.

由于我们从小到大枚举了最大因子KKK, 那么可能原先的数不能被小于等于KKK的数分解, 那么我们认为其最小因子的最大值为−INF-INFINF.


我们设一开始f1=1,∀i>1,fi=−INFf_1=1,\forall i>1,f_i=-INFf1=1,i>1,fi=INF.

我们从K=2K=2K=2开始, 从小到大枚举KKK.

K=kK=kK=k时, 由于我们要让因子分解地尽量地大, 那就等价于尽可能地不被分解fk=kf_k=kfk=k(如性质2可得).

我们考虑更新fif_ifi由于Ai=p⋅qA_i=p\cdot qAi=pq要求p,qp,qp,q均为AiA_iAi的因子, 我们直接从小到大枚举kkk的倍数.

假设, 当前需要更新的数为x=k⋅j(j>1)x=k\cdot j(j>1)x=kj(j>1), 那么由于性质2, 可得fj≤K,fk=Kf_j\leq K,f_k=KfjK,fk=K.

xxx分解为jjjkkk时, 其最小因子的最大值应当为fj(fj≤fk=K)f_j(f_j\leq f_k=K)fj(fjfk=K).

从小到大枚举KKK再进行上述更新即可, 当然由于我们让限制KKK逐步扩大, 那么fif_ifi的值也会随之变大, 因此我们可以用单调队列的方式进行更新.

时间复杂度为O(mlog⁡m)O(m\log m)O(mlogm), 如果使用set维护时间复杂度为O(mlog⁡2m)O(m\log^2m)O(mlog2m).


Code[O(mlog⁡2m)][O(m\log^2m)][O(mlog2m)]

由于懒得写单调队列了, 直接用一个set进行维护
const int maxm=5e6+10;
const int INF=1<<30;

int A[maxm],Ans,DP[maxm];
bool Visit[maxm];

void Solve(){
	static int N,M,i,j,To;
	read(N),read(M);
	for(i=1;i<=M;++i) Visit[i]=false; 
	for(i=1;i<=N;++i) read(A[i]),Visit[A[i]]=true; 
	set<pii> Now;
	DP[1]=1;
	for(i=2;i<=M;++i) DP[i]=-INF;
	for(i=1;i<=M;++i) if(Visit[i]) Now.insert({DP[i],i});
	Ans=1-Now.begin()->first;
	for(i=2;i<=M;++i){
		if(Visit[i]) Now.erase({DP[i],i}),Now.insert({i,i});
		DP[i]=i;
		int Tem=M/i;
		for(j=2;j<=Tem;++j){
			To=i*j;
			if(DP[To]<DP[j]){
				if(Visit[To]){
					Now.erase({DP[To],To});
					Now.insert({DP[j],To});
				}DP[To]=DP[j];
			}
		}Ans=min(Ans,i-Now.begin()->first);
	}
	printf("%d\n",Ans);
	return;
}
Tips: 本题难点在枚举顺序上, 当我们从小到大枚举的时候, 能获得到单调性这一特殊性质
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值