题目大意:给定一个序列a1,a2,……,an,求满足以下要求的最长子序列:1.该序列是严格上升的,即x[i] < x[i+1](1<=i<=k-1),2.任意两个相邻的元素是非互质的,即gcd(x[i],x[i+1])>1(1<=i<=k-1)。(1<=n,a[i]<=105)
思路:我们先将序列排序,满足严格上升的条件,然后可以很容易的列出一个动态转移方程:if(gcd(f[i],f[j])>1) f[i]=max(f[i],f[j]+1);(1<=j<i),但这个复杂度是O(n^2)的,无法通过,那么我们考虑优化。我们仔细观察这个式子(大雾,发现每个数只和他因数的倍数有关,再仔细观察又发现,对于一个因数x,他的k倍一定比他的i倍(i<k)大,而且第二大的是这个数的(k-1)倍,那么我们只需要枚举每个数的因数,每个数只和他前一个因数的倍数有关,这样复杂度就变成O(n·log(n))的了;
代码如下:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<cstdlib> #include<cstring> #include<vector> using namespace std; const int MaxN=200000; int n,a[MaxN],id[MaxN],tot[MaxN],f[MaxN],nmax,vis; vector<int>fir[MaxN]; int main(){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]),f[i]=1,tot[a[i]]=1; sort(a+1,a+n+1);nmax=a[n]; for(int i=1;i<=n;++i) id[a[i]]=i; for(int i=2;i<=nmax;++i){vis=0; for(int j=i;j<=nmax;j+=i){ if(tot[j]==1){ if(vis!=0) fir[id[j]].push_back(vis); vis=id[j]; } } } for(int i=1;i<=n;++i){ for(int j=0;j<fir[i].size();++j){ f[i]=max(f[fir[i][j]]+1,f[i]); } }nmax=0; for(int i=1;i<=n;++i) nmax=max(nmax,f[i]); printf("%d ",nmax); return 0; }