牛客IOI周赛23-普及组 /D(序列dp)
题面:
小L喜欢数和数列。小L称a1…ana_1…a_na1…an这些数为优秀的。小L称一个序列b1…bmb_1…b_mb1…bm为好的当且仅当:
1.对于任意的 i(1≤i<m)i (1 \leq i <m)i(1≤i<m),满足 bi<bi+1b_i<b_{i+1}bi<bi+1。
2.对于任意的 i(1≤i<m)i (1 \leq i <m)i(1≤i<m),满足 gcd(bi,bi+1)>1gcd(b_i,b_{i+1})>1gcd(bi,bi+1)>1。其中,gcd(x,y)gcd(x,y)gcd(x,y) 为 xxx 和 yyy 的最大公因数,即最大的 ddd,满足:d∣xd|xd∣x且d∣yd|yd∣y。
3.对于任意的 i(1≤i≤m)i (1 \leq i \leq m)i(1≤i≤m),bib_ibi这个数是优秀的。
现在,小L想知道最长的能称为好的的序列的长度是多少,容易证明这个长度是有穷的。
显然这是一个序列dp的问题:
先将序列从小到大排序,用
d
p
[
i
]
dp[i]
dp[i]前
i
i
i位合法序列的最大长度,
那么有转态转移方程。
d
p
[
i
]
=
max
j
<
i
,
g
c
d
(
a
[
i
]
,
a
[
j
]
)
!
=
1
(
d
p
[
i
]
,
d
p
[
j
]
+
1
)
dp[i]=\max_{j<i,gcd(a[i],a[j])!=1}(dp[i],dp[j]+1)
dp[i]=j<i,gcd(a[i],a[j])!=1max(dp[i],dp[j]+1)
时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。
现考虑优化dp过程,原dp转移过程中需要枚举所有
j
<
i
j<i
j<i的项且要求解
g
c
d
(
a
[
i
]
,
a
[
j
]
)
gcd(a[i],a[j])
gcd(a[i],a[j])。
可以考虑枚举
a
[
i
]
a[i]
a[i]的质因子,用
l
e
n
[
p
]
len[p]
len[p]表示包含素因子
p
p
p的
a
[
i
]
a[i]
a[i]中能组成合法序列的最大长度。
那么状态转移方程变化为:
d
p
[
i
]
=
max
p
∣
a
[
i
]
(
d
p
[
i
]
,
l
e
n
[
p
]
+
1
)
dp[i]=\max_{p|a[i]}(dp[i],len[p]+1)
dp[i]=p∣a[i]max(dp[i],len[p]+1)
p
p
p是
a
[
i
]
a[i]
a[i]的素因子。
在更新完
d
p
[
i
]
dp[i]
dp[i]后,我们需要更新
a
[
i
]
a[i]
a[i]所有素因子对应的
l
e
n
[
i
]
len[i]
len[i],即
l
e
n
[
p
]
=
max
p
∣
a
[
i
]
(
l
e
n
[
p
]
,
d
p
[
i
]
)
len[p]=\max_{p|a[i]}(len[p],dp[i])
len[p]=p∣a[i]max(len[p],dp[i])
#include<bits/stdc++.h>
using namespace std;
const int max_n=1e6+5;
int dp[max_n];
int prime[max_n];
int vis[max_n];
int a[max_n];
int n;
int cnt=0;
vector<int> f;
void init(void)
{
for(int i=2;i<max_n;i++)
{
if(!vis[i])prime[cnt++]=i;
for(int j=0;j<cnt&&i*prime[j]<max_n;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
int main(void)
{
int t;
init();
scanf("%d",&t);
while(t--)
{
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
f.clear();
int anss=-1;
for(int j=0;j<cnt&&prime[j]*prime[j]<=a[i];j++)
if(a[i]%prime[j]==0){
while(a[i]%prime[j]==0)a[i]/=prime[j];
anss=max(anss,dp[prime[j]]+1);
f.push_back(prime[j]);
if(a[i]==1)break;
}
if(a[i]!=1){
anss=max(anss,++dp[a[i]]);
f.push_back(a[i]);
}
ans=max(anss,ans);
for(int k=0;k<f.size();k++)
dp[f[k]]=max(dp[f[k]],anss);
}
cout<<ans<<endl;
}
}