bzoj 1978: [BeiJing2010]取数游戏 game 数学

题意

小 C 刚学了辗转相除法,正不亦乐乎,这小 P 又出来捣乱,给小 C 留了个 难题。 给 N 个数,用 a1,a2…an来表示。现在小 P 让小 C 依次取数,第一个数可以 随意取。假使目前取得 aj,下一个数取ak(k>j),则ak必须满足gcd(aj,ak)≥L。 到底要取多少个数呢?自然是越多越好! 不用多说,这不仅是给小 C 的难题,也是给你的难题。
N≤50 000,2≤L≤ai≤1 000 000;

分析

考虑dp,设f[i]表示从i开始往后取的最大方案,t[x]表示从x这个数往后取的最大方案。
对于f[i],枚举gcd(a[i],a[j])=d>=L,用t[d]+1来更新f[i]。
然后枚举所有f[i]的约数x,用f[i]来更新t[x]即可。

代码

#include
#include
#include
#include
#include
using namespace std;

const int N=50005;

int n,m,a[N],f[N],t[1000005];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int main()
{
    n=read();m=read();
    for (int i=1;i<=n;i++) a[i]=read();
    int ans=0;
    for (int i=n;i>=1;i--)
    {
        for (int j=1;j*j<=a[i];j++)
            if (a[i]%j==0)
            {
                if (j>=m) f[i]=max(f[i],t[j]+1);
                if (a[i]/j>=m) f[i]=max(f[i],t[a[i]/j]+1);
            }
        for (int j=1;j*j<=a[i];j++)
            if (a[i]%j==0) t[j]=max(t[j],f[i]),t[a[i]/j]=max(t[a[i]/j],f[i]);
        ans=max(ans,f[i]);
    }
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(数学)