1. 程式人生 > >F: 取數遊戲III (思維+簡單dp)

F: 取數遊戲III (思維+簡單dp)

F: 取數遊戲III

題目描述

小 C 剛學了輾轉相除法,正不亦樂乎,這小 P 又出來搗亂,給小 C 留了個 難題。 給 N 個數,用 a1,a2…an來表示。現在小 P 讓小 C 依次取數,第一個數可以 隨意取。假使目前取得 aj,下一個數取ak(k>j),則ak必須滿足gcd(aj,ak)≥L。 到底要取多少個數呢?自然是越多越好! 不用多說,這不僅是給小 C 的難題,也是給你的難題。

 

輸入

第一行包含兩個數N 和 L。 接下來一行,有 N 個數用空格隔開,依次是 a1,a2…an。

 

輸出

僅包含一行一個數,表示按上述取法,最多可以取的數的個數。

 

樣例輸入

5 6
7 16 9 24 6

 

樣例輸出

3

 

提示

選取 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

 

分析:

pos[j] 記錄約數為j的最“新”位置
dp[i] 記錄前i個數中以i作為最後一個數最多能取多少個
狀態轉移方程:
dp[i] = max(dp[pos[j]]+1,dp[i])
 
#include <bits/stdc++.h>

using
namespace std; const int maxn = 5000005; int pos[maxn]={0}; int dp[maxn]={0}; int a[maxn]={0}; int main() { int n,l; scanf("%d%d",&n,&l); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); } int tmp; int ans = 0; for(int i=1;i<=n;i++) { tmp = sqrt(a[i]);
for(int j=1;j<=tmp;j++) { if(a[i]%j==0) { if(j>=l) { dp[i] = max(dp[pos[j]]+1,dp[i]); } if(a[i]/j>=l) { int t = a[i]/j; dp[i] = max(dp[pos[t]]+1,dp[i]); } pos[j] = pos[a[i]/j] = i; } } ans = max(ans,dp[i]); } printf("%d",ans); return 0; }