**gcd promblem
如果没有NTF讲dp,我真想不到怎么dp,没想到dp可以这么用;
——————————————————————————————————————————————
先把需要的状态表示出来,我们一定需要gcd,所以dp中一定要有gcd,但这样会是
d
p
[
i
]
[
j
]
表
示
前
i
个
数
,
g
c
d
为
j
的
最
大
值
dp[i][j]表示前i个数,gcd为j的最大值
dp[i][j]表示前i个数,gcd为j的最大值
这样一定会MLE的
考虑优化第二维
1.
任意一个数
a
=
p
1
c
1
∗
p
2
c
2
∗
.
.
.
∗
p
n
c
n
(
p
1
,
p
2...
p
n
为
质
数
)
a=p1^{c1}*p2^{c2}*...*pn^{cn}(p1,p2...pn为质数)
a=p1c1∗p2c2∗...∗pncn(p1,p2...pn为质数) 一定可以取得最大值
∑
c
i
\sum{ci}
∑ci,而因为gcd不等时才有贡献,所以每次必会减少一个因子才有贡献,而因为当gcd=1时,价值为1,每次减少一个因子后变成1,则刚开始贡献即为原来因子和,并且一定能取到
如第一个数
a
=
2
5
∗
3
6
∗
5
8
a=2^5*3^6*5^8
a=25∗36∗58,接下来依次取(不一定按这种取法)
2
4
∗
3
6
∗
5
8
,
2
3
∗
3
6
∗
5
8
.
.
.
.
.
.
.
2^4*3^6*5^8,2^3*3^6*5^8.......
24∗36∗58,23∗36∗58.......才会有不同的gcd
2.
第
一
个
数
为
a
,
含
有
一
个
大
于
三
的
因
子
p
,
剩
下
因
子
的
乘
积
为
p
s
u
m
,
(
p
s
u
m
∗
p
=
a
)
第一个数为a,含有一个大于三的因子p,剩下因子的乘积为psum,(psum*p=a)
第一个数为a,含有一个大于三的因子p,剩下因子的乘积为psum,(psum∗p=a)
因为
2
2
<
p
2^2<p
22<p
则一定可以取到比原数小的数2^2*psum,使得
∑
c
i
更
大
\sum{ci}更大
∑ci更大
3.因为
3
2
>
2
3
3^2>2^3
32>23,所以一定可以找到
2
3
2^3
23(同2理)
这样我们将dp优化为
d
p
[
i
]
[
x
]
[
y
]
,
g
c
d
=
2
x
∗
3
y
,
0
<
=
y
<
=
1
dp[i][x][y],gcd=2^x*3^y,0<=y<=1
dp[i][x][y],gcd=2x∗3y,0<=y<=1
接下来就可以dp了
- 若 g c d 不 变 , 则 说 明 新 加 入 的 数 是 g c d 的 倍 数 , 此 时 前 i − 1 个 数 也 都 是 g c d 的 倍 数 , 直 接 继 承 若gcd不变,则说明新加入的数是gcd的倍数,此时前i-1个数也都是gcd的倍数,直接继承 若gcd不变,则说明新加入的数是gcd的倍数,此时前i−1个数也都是gcd的倍数,直接继承
- 若 g c d / 2 , 则 新 加 入 的 数 一 定 是 g c d / 2 的 倍 数 , 一 定 不 是 g c d 的 倍 数 ( 要 不 然 答 案 还 是 g c d ) 若gcd/2,则新加入的数一定是gcd/2的倍数,一定不是gcd的倍数(要不然答案还是gcd) 若gcd/2,则新加入的数一定是gcd/2的倍数,一定不是gcd的倍数(要不然答案还是gcd)
-
若
g
c
d
/
3
,
则
新
加
入
的
数
一
定
是
g
c
d
/
3
的
倍
数
,
不
是
g
c
d
的
倍
数
若gcd/3,则新加入的数一定是gcd/3的倍数,不是gcd的倍数
若gcd/3,则新加入的数一定是gcd/3的倍数,不是gcd的倍数
d p 代 码 , 其 中 c n t ( x ) = n / x dp代码,其中cnt(x)=n/x dp代码,其中cnt(x)=n/x:
dp[i][j][0]=(dp[i][j][0]
+dp[i-1][j+1][0]*(1ll*(cnt((1<<j))-cnt(1<<(j+1)))%mod)%mod
+dp[i-1][j][0]*(1ll*(cnt(1<<j)-i+1)%mod)%mod%mod
+dp[i-1][j][1]*(1ll*(cnt((1<<j))-cnt((1<<j)*3))%mod))%mod;
dp[i][j][1]=((dp[i][j][1]
+dp[i-1][j][1]*(1ll*(cnt(((1<<j))*3)-i+1)%mod)%mod)
+dp[i-1][j+1][1]*(1ll*(cnt(((1<<j)*3))-1ll*cnt(((1<<(j+1))*3)))%mod)%mod
)%mod;
——————————————————————————————————————————————
**#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n;
ll mod=1e9+7;
int dp[N][22][2];
int cnt(int x){return n/x;}
int main(){
scanf("%d",&n);
int up=log2(n);
dp[1][up][0]=1;
if((1<<up)/2*3<=n) dp[1][up-1][1]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<=up;j++){
dp[i][j][0]=(dp[i][j][0]
+dp[i-1][j+1][0]*(1ll*(cnt((1<<j))-cnt(1<<(j+1)))%mod)%mod
+dp[i-1][j][0]*(1ll*(cnt(1<<j)-i+1)%mod)%mod%mod
+dp[i-1][j][1]*(1ll*(cnt((1<<j))-cnt((1<<j)*3))%mod))%mod;
dp[i][j][1]=((dp[i][j][1]
+dp[i-1][j][1]*(1ll*(cnt(((1<<j))*3)-i+1)%mod)%mod)
+dp[i-1][j+1][1]*(1ll*(cnt(((1<<j)*3))-1ll*cnt(((1<<(j+1))*3)))%mod)%mod
)%mod;
}
}
printf("%d",dp[n][0][0]);
}**
——————————————————————————————————————————————
这题说明了,只要满足最优子结构,就可以dp,在dp时先把所有状态表示出来,发现不需要的状态再删除,知道满足数据范围即可