Description
Solution
Lemma 1
g i + 1 = t g i ( t ∈ N ∗ ) g_{i+1}=t\ g_i(t \in N^*) gi+1=t gi(t∈N∗),且 t ≤ 3 t \le 3 t≤3。
Certification 1
根据 gcd \gcd gcd的定义,不难发现 g i + 1 g_{i+1} gi+1一定是 g i g_i gi的约数。
例如,一个合法的 g g g序列是 { 12 , 6 , 6 , 6 , 6 , 3 , 2 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 } \{ 12,6,6,6,6,3,2,2,1,1,1,1,1,1,1,1\} {12,6,6,6,6,3,2,2,1,1,1,1,1,1,1,1}。如果我们将这个序列给缩一下,那么会变为 g ′ = { 12 , 6 , 3 , 2 , 1 } g'=\{12,6,3,2,1\} g′={12,6,3,2,1}。
此时 f ( p ) f(p) f(p)就是 g ′ g' g′的长度。当 f ( p ) f(p) f(p)最大时,我们肯定要贪心地让 g i + 1 ′ g'_{i+1} gi+1′比 g i ′ g'_i gi′小的倍数越少越好,否则 g ′ g' g′的长度必然被削减。
令这个小的倍数为 t ′ t' t′,不难发现 t ′ > 2 t'>2 t′>2。考虑当 t ′ = 4 t'=4 t′=4时,我们显然可以在其中加一项——例如 g ′ = { 24 , 12 , 3 , 1 } g'=\{24,12,3,1\} g′={24,12,3,1}可以优化为 g ′ = { 24 , 12 , 6 , 3 , 1 } g'=\{24,12,6,3,1\} g′={24,12,6,3,1},我们在其中添了一项 6 6 6,这样不仅没有影响 g ′ g' g′的合法性也使得 g ′ g' g′的长度变大。
扩展得,当 4 ≤ t ′ 4 \le t' 4≤t′时 g ′ g' g′一定不是最优。所以Lemma 1中 t ≤ 3 t \le 3 t≤3。
证毕。
Lemma 2
g i + 1 = t g i ( t ∈ N ∗ ) g_{i+1}=t\ g_i(t \in N^*) gi+1=t gi(t∈N∗),且 t ≤ 3 t \le 3 t≤3;更进一步的, t = 3 t=3 t=3的次数最多只有1次。
Certification 1
考虑到 2 3 ≤ 3 2 2^3 \le 3^2 23≤32,所以连续两次 t = 3 t=3 t=3肯定差于三次 t = 2 t=2 t=2,所以 t = 3 t=3 t=3的次数最多只有 1 1 1次。
考虑 d p dp dp。
状态设计: d p i , j , k dp_{i,j,k} dpi,j,k: 看到第 i i i个数,目前 gcd \gcd gcd为 2 j 3 k 2^j 3^k 2j3k。
状态转移:
①
gcd
\gcd
gcd不变。
d
p
i
,
j
,
k
=
(
⌊
n
p
⌋
−
i
+
1
)
d
p
i
−
1
,
j
,
k
dp_{i,j,k}=(\lfloor \frac n p \rfloor-i+1)dp_{i-1,j,k}
dpi,j,k=(⌊pn⌋−i+1)dpi−1,j,k
②
gcd
\gcd
gcd除以
2
2
2(
j
+
1
≤
⌊
log
n
⌋
j+1 \le \lfloor \log n \rfloor
j+1≤⌊logn⌋):
d
p
i
,
j
,
k
=
(
⌊
n
2
j
3
k
⌋
−
⌊
n
2
j
+
1
3
k
⌋
)
d
p
i
,
j
+
1
,
k
dp_{i,j,k}=(\lfloor \frac n {2^j 3^k} \rfloor-\lfloor \frac n {2^{j+1}3^k} \rfloor)dp_{i,j+1,k}
dpi,j,k=(⌊2j3kn⌋−⌊2j+13kn⌋)dpi,j+1,k
③
gcd
\gcd
gcd除以
3
3
3(
k
=
0
k=0
k=0):
d
p
i
,
j
,
k
=
(
⌊
n
2
j
3
k
⌋
−
⌊
n
2
j
3
k
+
1
⌋
)
d
p
i
,
j
,
k
+
1
dp_{i,j,k}=(\lfloor \frac n {2^j 3^k}\rfloor-\lfloor \frac n {2^j 3^{k+1}} \rfloor)dp_{i,j,k+1}
dpi,j,k=(⌊2j3kn⌋−⌊2j3k+1n⌋)dpi,j,k+1
为什么③的合法转移要求 k = 0 k=0 k=0呢?因为,对于一次③的转移相当于使 t t t成为了一次 3 3 3。根据之前的引理, t = 3 t=3 t=3的次数只能有 1 1 1次。
边界:
①
d
p
1
,
⌊
log
n
⌋
,
0
=
1
dp_{1,\lfloor \log n \rfloor,\ 0}=1
dp1,⌊logn⌋, 0=1
②
d
p
1
,
⌊
log
n
⌋
−
1
,
1
=
1
(
2
⌊
log
n
⌋
−
1
×
3
≤
n
)
dp_{1,\lfloor \log n \rfloor-1,\ 1}=1(2^{\lfloor \log n \rfloor-1} \times 3 \le n)
dp1,⌊logn⌋−1, 1=1(2⌊logn⌋−1×3≤n)
答案: d p n , 0 , 0 dp_{n,0,0} dpn,0,0。
总时间复杂度 O ( n log 2 n ) O(n \log_2 n) O(nlog2n)。
Summary
本题考查了性质挖掘能力与基本的 d p dp dp能力,可以说是一道不可多得的好题。
我在做这题的时候,性质全部挖掘了出来,可是 d p dp dp的状态设计却并不是最优,这导致我列出了一个关于下降幂的状态转移,从而时间复杂度多出了一个 n n n。所以说,我是不是很菜?
这还用问……
Code
①在实现的时候,你必须采用滚动数组,否则空间开不下。
②一些卡常技巧: 少取模,开
O
2
O2
O2。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int g1=25,g2=25,mod=1e9+7;
int n,L2,L3;
int dp[2][g1][2],bin[g1],sea[g2];
int get_log_two(int tmp){
int p=1,cnt=0;
while (p<=tmp) p*=2,cnt++;
return cnt-1;
}
int get_log_three(int tmp){
int p=1,cnt=0;
while (p<=tmp) p*=3,cnt++;
return cnt-1;
}
int add(int tmpx,int tmpy){return (tmpx+(max(tmpy,0ll)))%mod;}
void init(){
bin[0]=1,sea[0]=1;
for (int i=1;i<=20;i++) bin[i]=(bin[i-1]*2)%mod;
for (int i=1;i<=13;i++) sea[i]=(sea[i-1]*3)%mod;
}
signed main(){
cin>>n;init();
L2=get_log_two(n),L3=get_log_three(n);
dp[0][L2][0]=1;
if (((1ll<<L2)/2)*3<=n) dp[0][L2-1][1]=1;
for (int i=2;i<=n;i++){
for (int j=0;j<=L2;j++){
for (int k=0;k<2;k++){
int p=bin[j]*sea[k];
if (p>n) continue;
dp[1][j][k]=add(dp[1][j][k],((n/p)-i+1)*dp[0][j][k]);
if (j<L2) dp[1][j][k]=add(dp[1][j][k],((n/p)-(n/(2*p)))*dp[0][j+1][k]);
if (k==0) dp[1][j][k]=add(dp[1][j][k],((n/p)-(n/(3*p)))*dp[0][j][k+1]);
}
}
for (int j=0;j<=L2;j++){
for (int k=0;k<2;k++) dp[0][j][k]=dp[1][j][k],dp[1][j][k]=0;
}
}
cout<<dp[0][0][0]<<endl;
return 0;
}