设有 NNN 个正整数,记为 a1,a2,...,ana_1,a_2,...,a_na1,a2,...,an。现依次取数,第一个数可以 随意取。假设目前取得 aja_jaj,下一个数取ak(j<k)a_k(j<k)ak(j<k),则aka_kak必须满足(aj,ak)≥L(a_j,a_k)\ge L(aj,ak)≥L(这里括号是求最大公约数的简记)。 求最多可取几个数。
Input
第一行是两个正整数NNN和LLL。
第二行是NNN个以空格分开的正整数a1,a2,...,ana_1,a_2,...,a_na1,a2,...,an.
Output
仅一行,一个数,表示按上述取法,最多可以取的数的个数。
Sample Input
5 6
7 16 9 24 6
Sample Output
3
HINT
选取 333 个数 16,24,6. (16,24)=8,(24,6)=616,24,6.\;(16,24)=8,(24,6)=616,24,6.(16,24)=8,(24,6)=6.
2≤L≤ai≤106,∀i2\le L\le a_i\le 10^6,\forall i2≤L≤ai≤106,∀i;
30%30\%30% 的数据 N≤1,000N≤1,000N≤1,000;
100%100\%100% 的数据 N≤50,000N\le 50,000N≤50,000
本蒟蒻一开始想了一个O(n2logn)O(n^2\log n)O(n2logn)的算法:设F(i)F(i)F(i)表示第 iii 个数取的情况下从a1,a2,⋯ ,aia_1,a_2,\cdots, a_{i}a1,a2,⋯,ai中所能取的数的个数的最大值,则
F(i)=1+max1≤i′<iF(i′)F(i)=1+\max_{1\le i'<i}F(i')F(i)=1+1≤i′<imaxF(i′)
其中 i′i'i′ 满足(ai′,ai)≥L,F(1)=1(a_{i'},a_i)\ge L,F(1)=1(ai′,ai)≥L,F(1)=1。
看了hzwer的题解感觉十分精妙……
由于(ai′,ai)∣ai(a_i',a_i)\mid a_i(ai′,ai)∣ai,故只需考虑aia_iai的约数能整除的ai′a_{i'}ai′。不妨设
lasti(x)=max1≤i′<ii′last_i(x)=\max_{1\le i'<i}i'lasti(x)=1≤i′<imaxi′
其中 i′i'i′ 满足 x∣ai′x\mid a_{i'}x∣ai′.
现在F(i)F(i)F(i)可改写为 F(i)=maxx∣ai,x≥L{F(lasti(x))}+1F(i)=\max_{x\mid a_i,x\ge L}\{F(last_i(x))\}+1F(i)=x∣ai,x≥Lmax{F(lasti(x))}+1.
状态数O(n)O(n)O(n),转移O(n)O(\sqrt n)O(n),总时间复杂度O(nn)O(n\sqrt n)O(nn).
代码实现中会随着i
的递增不断更新last
数组,这样last
就不用开成222维了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn=50001;
int f[maxn];
int last[1000001];
int a[50001];
inline int read(){
int x=0;scanf("%d",&x);return x;
}
int n,l,ans;
int main(){
n=read();l=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i){
int &tmp=f[i];
for(int j=1;j*j<=a[i];++j){
if(a[i]%j) continue;
if(j>=l&&f[last[j]]+1>tmp) tmp=f[last[j]]+1;
if(a[i]/j>=l&&f[last[a[i]/j]]+1>tmp) tmp=f[last[a[i]/j]]+1;
last[j]=i;
last[a[i]/j]=i;
}
if(tmp>ans) ans=tmp;
}
printf("%d\n",ans);
return 0;
}