CF264B Good Sequences

CF264B Good Sequences

洛谷传送门

题意翻译

松鼠丽丝对序列很感兴趣,她对整数也有着爱好。它特别喜欢n个她称之为“好整数”的整数:a1,a2,……,an。(会输入)
现在,她对“好序列”很感兴趣。如果一个序列x1,x2,...,xk能够满足一下三个条件,那就是一个“好序列”:
1.该序列是严格上升的,即x[i] < x[i+1](1<=i<=k-1)
2.任意两个相邻的元素是非互质的,即gcd(x[i],x[i+1]) > 1 (1<=i<=k-1) (gcd即最大公约数)
3.所有的数字都是“好整数”
现在,请你找出长度最长的“好序列”

题解:

一开始想了一个裸的DP:设\(dp[i]\)表示结尾位置为\(i\)的最长好序列。那么转移就是

\[dp[i]=\max(dp[j])+1\quad(j\in(1,i-1),\gcd(a[i],a[j]>1)) \]

复杂度是\(O(n^2\log n)\)

在21号点折戟。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int n,ans;
int a[maxn];
int dp[maxn];
//dp[i]表示以i结尾的最长好序列长度。
int gcd(int a,int b)
{
    return !b?a:gcd(b,a%b);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        dp[i]=1;
    }
    for(int i=2;i<=n;i++)
        for(int j=1;j<i;j++)
            if(gcd(a[i],a[j])>1)
                dp[i]=max(dp[i],dp[j]+1);
    for(int i=1;i<=n;i++)
        ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}

考虑如何优化转移:这个log大概率是省不掉的,只能优化两重枚举,我们可不可以找到一个唯一的转移过去的方式或者使转移时枚举数尽量少。我们考虑从GCD入手。

我们发现,我们只需要找到当前枚举到的状态前的最大合法dp值即可。而这个合法DP还应该和GCD有关(因为数列保证有序)。而GCD的一般处理方式是拆成因子——那就再设一个状态:\(f[i]\)表示含有因子\(i\)\(a[x]\)中的最大\(dp[x]\)。这个转移也是无后效性的,所以可以在DP过程中同步更新。这样,每次转移就只需要枚举这个数的所有质因子。大大优化了枚举效率。

代码:

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=1e5+5;
int n,ans,maxx;
int a[maxn];
int dp[maxn];
//dp[i]表示以i结尾的最长好序列长度。
int f[maxn];
//f[i]表示所有含有因子i的a[x]的最大dp[x]
int prime[maxn],tot;
bool v[maxn];
vector<int> fac[maxn];
void init()
{
    for(int i=2;i<=maxx;i++)
	{
		if(!v[i])
			prime[++tot]=i;
		for(int j=1;j<=tot&&i*prime[j]<=maxx;j++)
		{
			v[i*prime[j]]=1;
			if(i%prime[j]==0)
				break;
		}
	}
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        dp[i]=1;
        maxx=max(maxx,a[i]);
    }
    init();
    for(int i=1;i<=tot;i++)
		for(int j=prime[i];j<=maxx;j+=prime[i])
			 fac[j].push_back(prime[i]);
    for(int i=1;i<=n;i++)
    {
        int tmp=0;
        for(int j=0;j<fac[a[i]].size();j++)
            tmp=max(tmp,f[fac[a[i]][j]]);
        dp[i]=max(dp[i],tmp+1);
        for(int j=0;j<fac[a[i]].size();j++)
            f[fac[a[i]][j]]=max(f[fac[a[i]][j]],dp[i]);
    }
    for(int i=1;i<=n;i++)
        ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}
posted @ 2020-11-30 15:47  Seaway-Fu  阅读(60)  评论(0编辑  收藏  举报