最大公约数 [动态规划]

最 大 公 约 数 最大公约数


正 解 部 分 \color{red}{正解部分}

最优情况中, g c d gcd gcd 每次变化都是 质因数分解中的一个质因子次数减少 1 1 1, 所以 f m a x ( n ) f_{max}(n) fmax(n) [ 1 , n ] [1, n] [1,n] 中 质因数分解质数次幂的和 的 最大值 加上 1 1 1.

首先 f m a x ( n ) f_{max}(n) fmax(n) 分解质因数后一定是 2 x 3 y 2^x3^y 2x3y 的形式, 其中 x ∈ N , y ∈ { 0 , 1 } x \in \N, y \in \{0, 1\} xN,y{0,1}
假设存在 t > 3 t > 3 t>3, 使得 f m a x ( n ) f_{max}(n) fmax(n) 分解质因数后为 t 2 x 3 y t2^x3^y t2x3y, 则 t t t 可以替换为 2 ? 2^? 2?,答案不会更差, 因此 f m a x ( n ) = ⌊ log ⁡ 2 n ⌋ + 1 f_{max}(n) = \lfloor \log_2n \rfloor + 1 fmax(n)=log2n+1 .

接下来计算 f ( p ) = f m a x ( n ) f(p) = f_{max}(n) f(p)=fmax(n) p p p 的个数, 考虑 从左往右 填数, 前面的数字一定是 2 x 3 y 2^x3^y 2x3y 的形式,
F [ i , x , y ] F[i, x, y] F[i,x,y] 表示 填了前 i i i 个数字, 前 i i i 个数字的 g c d gcd gcd 2 x 3 y 2^x3^y 2x3y 的方案数,

初值

  • F [ 1 , ⌊ log ⁡ 2 N ⌋ , 0 ] = 1 F[1, \lfloor \log_2 N \rfloor, 0] = 1 F[1,log2N,0]=1
  • F [ 1 , ⌊ log ⁡ 2 N ⌋ − 1 , 1 ] = 1                ( 2 ⌊ log ⁡ 2 N ⌋ − 1 × 3 ≤ N ) F[1, \lfloor \log_2 N \rfloor - 1, 1] = 1\ \ \ \ \ \ \ \ \ \ \ \ \ \ (2^{\lfloor \log_2 N \rfloor - 1}\times3 \le N) F[1,log2N1,1]=1              (2log2N1×3N)

转移 时使得 x x x y y y 至多其中一个减一,

  • x , y x, y x,y 不变: F [ i , x , y ]   + = F [ i − 1 , x , y ] × ( ⌊ N 2 x 3 y ⌋ − ( i − 1 ) ) F[i, x, y] \ += F[i-1, x, y] \times \left( \lfloor \frac{N}{2^x3^y} \rfloor -(i-1) \frac{}{}\right) F[i,x,y] +=F[i1,x,y]×(2x3yN(i1))

  • x x x 1 1 1:      F [ i , x , y ]   + = F [ i − 1 , x + 1 , y ] × ( ⌊ N 2 x 3 y ⌋ − ⌊ N 2 x + 1 3 y ⌋ ) \ \ \ \ F[i, x, y]\ + = F[i-1, x+1,y] \times \left( \lfloor \frac{N}{2^x3^y} \rfloor - \lfloor \frac{N}{2^{x+1}3^y}\rfloor \right)     F[i,x,y] +=F[i1,x+1,y]×(2x3yN2x+13yN)

  • y y y 1 1 1:      F [ i , x , y ]   + = F [ i − 1 , x , y + 1 ] × ( ⌊ N 2 x 3 y ⌋ − ⌊ N 2 x 3 y + 1 ⌋ ) \ \ \ \ F[i, x, y]\ + = F[i-1, x, y+1]\times\left( \lfloor \frac{N}{2^x3^y} \rfloor - \lfloor \frac{N}{2^x3^{y+1}}\rfloor \right)     F[i,x,y] +=F[i1,x,y+1]×(2x3yN2x3y+1N)

最后答案即为 F [ N , 0 , 0 ] F[N, 0, 0] F[N,0,0] .


实 现 部 分 \color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register

const int mod = 1e9 + 7;
const int maxn = 1e6 + 5;

int N;
int F[maxn][22][2];

int Ksm(int a, int b){ int s=1; while(b){ if(b&1)s=1ll*s*a%mod; a=1ll*a*a%mod;b>>=1; } return s; }

int main(){
        scanf("%d", &N); int t = log2(N);
        F[1][t][0] = 1; if(Ksm(2, t-1)*3 <= N) F[1][t-1][1] = 1; 
        for(reg int i = 2; i <= N; i ++)
                for(reg int x = 0; x <= t; x ++)
                        for(reg int y = 0; y <= 1; y ++){
                                int &cur = F[i][x][y], t1 = Ksm(2,x)*Ksm(3,y), t2 = Ksm(2,x+1)*Ksm(3,y), t3 = Ksm(2,x)*Ksm(3,y+1);
                                if(N/t1 > i-1) cur = (cur + 1ll*F[i-1][x][y]*(N/t1-i+1)%mod) % mod;
                                if(x != t) cur = (cur + 1ll*F[i-1][x+1][y]*(N/t1 - N/t2)%mod) % mod;
                                if(y != 1) cur = (cur + 1ll*F[i-1][x][y+1]*(N/t1 - N/t3)%mod) % mod;
                        }
        printf("%d\n", F[N][0][0]);
        return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值