[dmy671]
#671. 优美!最长上升子序列
- 时间限制:1 s
- 空间限制:1024 MB
多组数据。
每组将给定一个数组。派派希望从中选择一个递增的子序列,越长越好。
但派派认为,这样选出来的子序列依然不够「优美」,形式化的讲,派派希望选择的下标(从 $1$ 开始)需要满足 $$ 𝑖_1∣𝑖_2∣𝑖_3∣⋯∣𝑖_𝑘 $$ 其中 $𝑎|𝑏$ 表示整除, 即 $𝑎$ 是 $𝑏$ 的约数。
请你帮助派派完成任务吧!
注:子序列的含义不再赘述。
输入格式
第一行一个整数 $𝑇$,表示接下来有 $𝑇$ 组数据。
每组数据包含两行,第一行包含一个整数 $N$。
随后一行,包含 $𝑁$ 个整数,表示原数组 $\{𝐴\}$。
输出格式
对于每组数据,输出一行,包含一个数,表示能选出的「优美」的最长上升子序列长度。
数据规模
- $1≤𝑇≤100$
- $1≤𝑁≤10^6$,但保证 $\sum\limits_{𝑖=1}^𝑇𝑁_𝑖≤10^6$
- $1≤𝐴_𝑖≤10^9$
样例输入
4
4
1 4 6 7
2
2 2
10
1 2 3 4 5 6 7 8 9 10
10
10 9 8 6 5 2 3 1 2 1
样例输出
3
1
4
1
解释:
对于第一组数据,能选择的「优美」最长上升子序列为 $\{𝐴_1,𝐴_2,𝐴_4\}=\{1,4,7\}$。
对于第三组数组,选择 $\{𝐴_1,𝐴_2,𝐴_4,𝐴_8\}=\{1,2,4,8\}$。
对于第四组数据,可选择的「优美」最长上升子序列长度为 $1$。
题目解析
- 设 $dp[i]$ 为以下标 $i$ 结尾的「优美」的最长上升子序列的长度.
- 对于第 $i$ 个数 $A_i$ 来说,若存在其后的某个下标 $j$,满足 $i \mid j$ 和 $A_j>A_i$(即序列后面有一个数 $A_j$,可以接在以下标 $i$ 结尾的「优美」的上升子序列的后面),则有状态转移方程: $$ dp[j]=\max(dp[j],dp[i]+1) $$
- $j$ 必须为 $i$ 的倍数,故可以通过循环遍历它.
- 最初状态有 $dp[i]=1,\; 1\leqslant i \leqslant N$.
- 时间复杂度为 $O(\frac{N}{1}+\frac{N}{2}+\frac{N}{3}+\cdots+\frac{N}{N})\approx O(n\log n)$.
即 $i$ 的倍数在$1\sim N$中有 $\frac{N}{i}$ 个.
代码展示
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+5;
int a[N],dp[N];
void solve(){
int n,ans=0; scanf("%d",&n);
for(int i=1;i<=n;++i){
dp[i]=1;
scanf("%d",&a[i]);
}
for(int i=1;i<=n;++i){
ans=max(ans,dp[i]);
for(int j=i+i;j<=n;j+=i)
if(a[j]>a[i]) dp[j]=max(dp[j],dp[i]+1);
}
printf("%d\n",ans);
}
int main(){
int T; scanf("%d",&T); while(T--) solve();
return 0;
}
浙公网安备 33010602011771号