您的位置:首页 > 其它

【bzoj1978】【BeiJing2010】取数游戏 game【递推】

2015-04-25 20:40 357 查看
小 C 刚学了辗转相除法,正不亦乐乎,这小 P 又出来捣乱,给小 C 留了个 难题。 给 NN 个数,用 a1,a2,...,ana_1,a_2,...,a_n来表示。现在小 P 让小 C 依次取数,第一个数可以 随意取。假使目前取得 aja_j,下一个数取ak(k>j)a_k(k>j),则aka_k必须满足gcd(aj,ak)≥Lgcd(a_j,a_k)\ge L。 到底要取多少个数呢?自然是越多越好! 不用多说,这不仅是给小 C 的难题,也是给你的难题。

Input

第一行包含两个数NN和LL。接下来一行,有NN个数用空格隔开,依次是 a1,a2,...,ana_1,a_2,...,a_n.

Output

仅包含一行一个数,表示按上述取法,最多可以取的数的个数。

Sample Input

5 6

7 16 9 24 6

Sample Output

3

HINT

选取 3个数16、24、6。gcd(16,24)=8,gcd(24,6)=6。

2≤L≤ai≤1 000 000;

30% 的数据N≤1000;

100% 的数据 N≤50 000

本蒟蒻一开始想了一个O(n2logn)O(n^2\log n)的算法:设F[i]表示第i个数取的情况下1到i最多能取几个数,则F[i]=max{F[j]}+1(j<i,gcd(a[j],a[i])≥L)F[i]=\max\left\{F[j]\right\}+1\quad(j

看了hzwer的题解感觉十分精妙……

因为gcd(a[i],a[j])一定是a[i]的约数,所以只用考虑能被a[i]的约数整除的a[j];然后因为这个a[j]肯定是越靠后越好(至少不会比放在前面差),所以设一个last[x]表示数列中所有能被x整除的a[j]中最靠后的那个位置,用数学语言描述就是last[x]=max{j}∣a[j]modx=0,j<i\text{last}[x]=\max\{j\}\mid \;a[j]\mod x=0,j

i是什么呢?F[i]=max{F[last[x]]}+1,满足x∣a[i]且x≥LF[i]=max\{F[last[x]]\}+1,满足 x\mid a[i] 且 x\ge L.

状态数O(n)O(n),转移O(n√)O(\sqrt n),总时间复杂度O(nn√)O(n\sqrt n).

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn=50001;
int f[maxn];
int last[1000001];
int a[50001];
inline int read(){
int x=0;scanf("%d",&x);return x;
}
int n,l,ans;
int main(){
n=read();l=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i){
int &tmp=f[i];
for(int j=1,t=(int)floor(sqrt((double)a[i]));j<=t;++j){
if(a[i]%j) continue;
if(j>=l&&f[last[j]]+1>tmp) tmp=f[last[j]]+1;
if(a[i]/j>=l&&f[last[a[i]/j]]+1>tmp) tmp=f[last[a[i]/j]]+1;
last[j]=i;
last[a[i]/j]=i;
}
if(tmp>ans) ans=tmp;
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: