CF1174E (Ehab and the Expected GCD Problem) 题解——数论与dp的完美结合

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(tN),且 t ≤ 3 t \le 3 t3

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' 4t g ′ g' g一定不是最优。所以Lemma 1中 t ≤ 3 t \le 3 t3

证毕。

Lemma 2

g i + 1 = t   g i ( t ∈ N ∗ ) g_{i+1}=t\ g_i(t \in N^*) gi+1=t gi(tN),且 t ≤ 3 t \le 3 t3;更进一步的, t = 3 t=3 t=3的次数最多只有1次

Certification 1

考虑到 2 3 ≤ 3 2 2^3 \le 3^2 2332,所以连续两次 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=(pni+1)dpi1,j,k
gcd ⁡ \gcd gcd除以 2 2 2( j + 1 ≤ ⌊ log ⁡ n ⌋ j+1 \le \lfloor \log n \rfloor j+1logn): 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=(2j3kn2j+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=(2j3kn2j3k+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,logn1, 1=1(2logn1×3n)

答案: 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值