1. 程式人生 > 其它 >CF1174E (Ehab and the Expected GCD Problem) 題解——數論與dp的完美結合

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=tgi(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=tgi(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;
}