加载中...

[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;
}
posted @ 2022-04-15 13:58  ZQYang  阅读(50)  评论(0)    收藏  举报