dp——gcd problem

博客探讨了一种使用动态规划(DP)解决最大GCD(最大公约数)问题的方法。通过分析数的质因数分解,优化了状态表示,避免了内存限制。博主指出,只要满足最优子结构,就可以应用DP。最终,将状态简化为dp[i][x][y],表示前i个数的最大GCD为2x * 3y。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

**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]igcdj
这样一定会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=p1c1p2c2...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=253658,接下来依次取(不一定按这种取法) 2 4 ∗ 3 6 ∗ 5 8 , 2 3 ∗ 3 6 ∗ 5 8 . . . . . . . 2^4*3^6*5^8,2^3*3^6*5^8....... 243658233658.......才会有不同的gcd

2. 第 一 个 数 为 a , 含 有 一 个 大 于 三 的 因 子 p , 剩 下 因 子 的 乘 积 为 p s u m , ( p s u m ∗ p = a ) 第一个数为a,含有一个大于三的因子p,剩下因子的乘积为psum,(psum*p=a) appsumpsump=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=2x3y,0<=y<=1
接下来就可以dp了

  • 若 g c d 不 变 , 则 说 明 新 加 入 的 数 是 g c d 的 倍 数 , 此 时 前 i − 1 个 数 也 都 是 g c d 的 倍 数 , 直 接 继 承 若gcd不变,则说明新加入的数是gcd的倍数,此时前i-1个数也都是gcd的倍数,直接继承 gcdgcdi1gcd
  • 若 g c d / 2 , 则 新 加 入 的 数 一 定 是 g c d / 2 的 倍 数 , 一 定 不 是 g c d 的 倍 数 ( 要 不 然 答 案 还 是 g c d ) 若gcd/2,则新加入的数一定是gcd/2的倍数,一定不是gcd的倍数(要不然答案还是gcd) gcd/2gcd/2gcdgcd
  • 若 g c d / 3 , 则 新 加 入 的 数 一 定 是 g c d / 3 的 倍 数 , 不 是 g c d 的 倍 数 若gcd/3,则新加入的数一定是gcd/3的倍数,不是gcd的倍数 gcd/3gcd/3gcd
    d p 代 码 , 其 中 c n t ( x ) = n / x dp代码,其中cnt(x)=n/x dpcnt(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时先把所有状态表示出来,发现不需要的状态再删除,知道满足数据范围即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值