洛谷链接:
https://www.luogu.org/problemnew/show/CF264B
codeforces题号: CF264B Good Sequences
这也是来自vjudge的一道好题。
刚开始看到的时候想到了LIS,但是因为n<=10^5,所以N^2的LIS就肯定行不通了(但是考场上实在想不出来暴力也是必须的)
然后我就又想到了LIS的nlog(n)优化,但是这里需要想一下,因为nlog(n)的LIS是使d[len]尽量的小,这样才可以往后接更多的,但是这里并非这个数字越小,往后可以接的数字越多,因为因子的个数是不存在单调性的,所以这个方法也否决了。
如果大家希望学习一下nlog(n)的LIS,可以参考这里:
https://www.cnblogs.com/PencilWang/p/5917717.html
然后我们可以套用LIS的一部分思想,我们在LIS中,F[i]表示以A[i]结尾的最长上升子序列,我们这里的F[i]就可以表示成到现在为止以i这因子为结尾可接上的最长的LIS的长度,那么显而易见,我们刚开始只需要预处理出所有数的所有因子,但是这里还有一个注意点,因为两个数即使互质,也有一个公因数1,所以1不能作为这个数的因子去记录。
然后就开始了我们的重点部分:
①先O(n)扫这整一个数组,当扫到第i个数的时候,将之前预处理出来的所以i的因子都提取出来,并++f[因子],这样就可以算出i的每一个因子到现在为止可以接到的最长的LIS的长度。同时记录一下最大的f[因子],记为maxnow
②然后再对应第i个数的因子再扫出来一遍,对应dp[因子]=maxnow,因为接下来的一个数只要和当前的这个数有一个因子(不能为1)相同,则这两个数就不是互质的,就可以接在这一个数字的后面,所以所有的f[当前这个数的因子]都等于maxnow。然后最后只需要从1到a[n]扫一遍就可以得出最长的子序列。
这是我的AC代码:供大家参考。
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
int n,maxx,anss;
vector<int>ans[200001];
int a[1000001];
int f[1000001];
int main(){
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
for (int i=1;i<=n;++i){
for (int j=2;j<=round(sqrt(a[i]));++j)
if (a[i]%j==0){
ans[i].push_back(j);
if (j*j!=a[i])
ans[i].push_back(a[i]/j);
}
ans[i].push_back(a[i]);
}
for (int i=1;i<=n;++i){
int nowmax=0;
for (int j=0;j<=ans[i].size()-1;++j){
++f[ans[i][j]];
if (f[ans[i][j]]>nowmax) nowmax=f[ans[i][j]];
}
for (int j=0;j<=ans[i].size()-1;++j)
if (f[ans[i][j]]<nowmax) f[ans[i][j]]=nowmax;
}
for (int i=1;i<=100010;++i)
if (f[i]>anss) anss=f[i];
printf("%d",anss);
}