IOI周赛23D 小L的数列(dp优化)
小L的数列
https://ac.nowcoder.com/acm/contest/11164/D
前言
比赛的时候只得了80分,想不出怎么优化,看了榜1大佬的思路明白了一些东西,才有了这篇题解(⊙﹏⊙)
题目描述
解题思路
1.容易看出,我们可以将数组排序后,进行dp,假设当前位置是第i个数,可以枚举前i-1个数,取与i的最大公约数大于1的数j,则f[i]=max(f[i],f[j]+1).这样可以得80分。(其余两个点超时)
2.接下来就需要考虑怎样优化dp的过程,暴力的方法一大部分时间花在
了枚举前i-1个数上,如果我们分解质因数,每次转移只需要枚举这个数的质因子即可。
AC代码
#include <bits/stdc++.h> #define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0) #define maxn 100010 using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int MOD=1000000007; const double pi=acos(-1.0); inline int lowbit(int x){ return x&(-x);} int a[maxn],mx[maxn],f[maxn]; //mx[i]表示i的最大质因子,f[i]表示以i结尾的序列的长度 int p[maxn]; //p存枚举到的数的质因子 bool vis[maxn]; int main() { io; //用埃氏筛找每个数的最大质因子,找最大值因子而不是最小质因子是为了方便质因数分解 for(int i=2;i<maxn;i++){ if(vis[i]) continue; for(int j=i;j<maxn;j+=i){ vis[j]=true; mx[j]=i; } } int t; cin>>t; while(t--){ memset(f,0,sizeof(f)); int n,ans=0; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+n+1); for(int i=1;i<=n;i++){ int r=0,maxx=0; //枚举每一个质因子,找以a[i]结尾的序列最大长度 while(a[i]>1){ int tmp=mx[a[i]]; while(a[i]%tmp==0) a[i]/=tmp; p[++r]=tmp; //保存该数的质因子 maxx=max(maxx,f[tmp]+1); ans=max(ans,maxx); } //更新以该数质因子结尾的序列长度,如在样例1中,遍历到2时,f[2]=1,遍历到3时,f[3]=1 //遍历到6时,f[2]=3(因为还有4),f[3]=2,一个数对每一个以其质因子结尾的序列都会有贡献 for(int j=1;j<=r;j++) f[p[j]]=max(f[p[j]],maxx); } cout<<ans<<endl; } return 0; }