CF804 E- Three Days Grace
Problem Statement
T T T组询问, 每组询问给一个数组 A i A_i Ai和两个整数长度 N N N及其值域 [ 1 , M ] [1,M] [1,M].
你可以进行如下操作
- 选择一个数 A i A_i Ai若满足 A i = p ⋅ q ( p , q ≠ 1 ) A_i=p\cdot q(p,q\neq 1) Ai=p⋅q(p,q=1)则可以删去 A i A_i Ai把 p , q p,q p,q加入数组.
- 你可以无数次地进行上述操作
问数组中的极差最小是多少, 即 max ( A i ) − min ( A i ) \max(A_i)-\min(A_i) max(Ai)−min(Ai)在若干次操作后的最小值.
Solution
假设当前最大因子为 K K K,我们考虑从小到大枚举最大的因子 K K K.
则 ∀ i , A i ≤ K \forall i,A_i\leq K ∀i,Ai≤K时, 我们需要让满足条件的 A i A_i Ai最大.
我们不妨设 f i f_i fi表示在最大因子不超过 K K K的条件下, 数 i i i的可被分解为的最小因子的最大值为 f i f_i fi(即 6 = 2 × 3 6=2\times 3 6=2×3则当 K = 3 K=3 K=3时 f 6 = 2 f_6=2 f6=2).
- 性质1: 显然有 f i ≤ K f_i\leq K fi≤K.
- 性质2: 由于操作是将 A i = p ⋅ q ( p , q ≠ 1 ) A_i=p\cdot q(p,q\neq 1) Ai=p⋅q(p,q=1), 显然进行一次操作后 A i A_i Ai会被分解称为两个更小的数, 即满足 p , q ≤ A i p,q\leq A_i p,q≤Ai.
由于我们从小到大枚举了最大因子 K K K, 那么可能原先的数不能被小于等于 K K K的数分解, 那么我们认为其最小因子的最大值为 − I N F -INF −INF.
我们设一开始 f 1 = 1 , ∀ i > 1 , f i = − I N F f_1=1,\forall i>1,f_i=-INF f1=1,∀i>1,fi=−INF.
我们从 K = 2 K=2 K=2开始, 从小到大枚举 K K K.
当 K = k K=k K=k时, 由于我们要让因子分解地尽量地大, 那就等价于尽可能地不被分解 f k = k f_k=k fk=k(如性质2可得).
我们考虑更新 f i f_i fi由于 A i = p ⋅ q A_i=p\cdot q Ai=p⋅q要求 p , q p,q p,q均为 A i A_i Ai的因子, 我们直接从小到大枚举 k k k的倍数.
假设, 当前需要更新的数为 x = k ⋅ j ( j > 1 ) x=k\cdot j(j>1) x=k⋅j(j>1), 那么由于性质2, 可得 f j ≤ K , f k = K f_j\leq K,f_k=K fj≤K,fk=K.
当 x x x分解为 j j j与 k k k时, 其最小因子的最大值应当为 f j ( f j ≤ f k = K ) f_j(f_j\leq f_k=K) fj(fj≤fk=K).
从小到大枚举 K K K再进行上述更新即可, 当然由于我们让限制 K K K逐步扩大, 那么 f i f_i fi的值也会随之变大, 因此我们可以用单调队列的方式进行更新.
时间复杂度为 O ( m log m ) O(m\log m) O(mlogm), 如果使用set维护时间复杂度为 O ( m log 2 m ) O(m\log^2m) O(mlog2m).
Code [ O ( m log 2 m ) ] [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;
}