設f(n,m) 表示剩余n 個石子且這次最多取m 時的NP 狀態,顯然這個函數在m 上是單調的,同時狀態的轉移產生一條對角線且隨著n 的增大逐漸右移,故從下到上可以在O(n) 下算出某個點的狀態。
然而這并沒什么卵用,因為這道題的n≤10 8 。
考慮k=2 時的情況,P 點集合為斐波那契數列。觀察這個規律的證明: 由Zeckendorf定理可知,任意正整數可以表示為不相鄰的斐波那契數的和且這個組合對每個數唯一。設MIN(x) 表示x 用斐波那契數分解之后的最小的數,以下欲證f(n)=min{i|f(n,i)=P}=MIN(n) 。 顯然f(2)=1=MIN(2) ,以下對n 進行歸納。 對于0<x<MIN(n) ,注意到MIN(n?x)=MIN(MIN(n)?x) 。 (1)因為MIN(MIN(n)?x)≤2x ,故0<x<MIN(n) 的轉移一定達到N 點。 (2)因為MIN(n-MIN(n))>2MIN(n)MIN(n?MIN(n))>2MIN(n) ,故此時轉移到了PP 點。 其中(1)的證明很模糊,將會為k/ge 2k≥2 的情況進行詳細的證明,故略去。
從k=2k=2 的證明想到,為任意的kk 構造一個數列AA ,使其可以同樣地唯一表示[1,n][1,n] 的數同時相鄰數比大于k 。構造很簡單: 設X i 表示A i 可以分解[1,X i ] 的數,那么A i+1 =X i +1 ,X i+1 =X i +1+X j |max{j|A i+1 A j >k} 。 X i+1 X i+1 A i+2 ?1A i+2 A i =X i +X j +1=A i+1 +X j =A i+1 +A j+1 ?1=A i+1 +A j+1 =A i?1 +A j |min{j|A i?1 A j ≤k} 以下證明對于任意A i =a+b ,MIN(a)b ≤k 。 設A j i 表示對于A i ?1 的從小到大第i 個分解。 從b=1 開始推導,此時b 與MIN(A i ?b) 都會落在[1,k] 上,故成立。 此后產生A j 1 次減1,期間顯然成立。 當b=A j 1 +1 時,a 在之前的MIN 被完全消去,也就是說,此時的MIN(a) 變為A j 2 ,同時,由于j 1 =max{j|A j 2 A j >k} ,故此時kb≥A j 2 =MIN(a) 。 而之后在A j 2 被完全消去之前,都保持滿足kb≥MIN(a) ,因為這期間產生的所有MIN 都小于A j 2 。 在A j 2 被完全消去時,推導同對A j 1 的推導,即當A j i 被消去時,b=A j i +1 。
故對于k>2 ,可以采用與k=2 時的證明相同的方法證明f(n)=MIN(n) 。 那么生成了A 之后就可以貪心分解n ,即可得出結果。 生成A 所需的復雜度為O(|A|) : A i A i?1 =A i?1 A i?1 +A j A i?1 =1+A j A i?1 ≥1+1k 故總的復雜度為O(log 1+1k n) 。
#include<iostream>using namespace std;typedef long long ll;int ar[2000010],n,k,ct=0;void cl(){ int i,a;for(scanf("%d %d",&n,&k),ar[1]=1,i=2,a=1;ar[i-1]+1<n;++i){ for(;(ll)ar[a]*k<ar[i-1];++a); ar[i]=ar[a]+ar[i-1]; }