1. 程式人生 > 其它 >K-good number Theory + 數學問題

K-good number Theory + 數學問題

這道題是我做CodeTon Round1時的D題,總的來看思路很重要,有幾個比較明顯的切入問題的角度,要選擇到最優的那個;

先看題目:

 

 我們可以發現,這道題的描述一目瞭然,就是說我們能不能找k個數的和正好等於我們輸入的n;

 但是有對於這k個數的限制:

  ·k>=2

  `k[i]%k 不相同

 

  所以我們粗略的想想就可以想到,既然這k個數的關於k的餘數都不相同,且為k個正數,所以我們就可以這麼想這k個數是在1,2,3……,k的基礎上進行的修改(+k || +2k || +3k……),所以我們就可以得到這樣一個一定正確的式子:

我們先令dp[i]為i的高斯求和,k是我們最後的ans:

if ( (n - dp[k]) % k == 0 ) ans = k;

解釋一下:因為n能和這個高斯和的差能被k整除得到x,說明我只要對於1~k中的任意x個數加上一個k或者對一個數加上xk都能得到這k個數;

 

單看這個分析是正確的,但是這道題不能這樣去做是因為n的資料範圍太大了!!!如果我要得到這麼dp[k]去match 10^18這個數量級的話,根據高斯求和,我起碼要開10^9這麼大的陣列,而且還得從前往後遍歷,這很明顯是不正確的;

 

所以我們對於一個數據很大的問題我們肯定是能找到某種規律是他們普遍適用的,或者說我們能逐漸將資料縮小到一個我們可以接受的範圍,而且這個縮小資料的過程也一定不能是一個一個往下縮,一般採取log級別的縮減;這是解決這種問題的總體思路;

 

所以我會自然而然地想到我們分成奇數和偶數,然後就可以發現,當n為奇數時,一定可以取k = 2,因為3k + 2是k取2時所能涵蓋的所有數,即除了1之外的奇數;那麼奇數問題解決了,我們就會去想解決偶數問題 , 但是我很難對這些偶數進行分類,只能根據樣例1猜測是不是所有的2的指數冪的數都只能輸出-1;

 

雖然上面的兩個思路都沒有完整的求解出我們所要的答案,但是他對於找出正確答案是有借鑑作用的,我們先去看到之前我們得出的那個充分必要條件,並將其中的dp用高斯求和展開可得 ( n - (1+k)*k/2 ) 是k的整數倍即可,然後我們可以根據數學輕鬆得出n和(1+k)*k/2 是k的整數倍就行了唄,由後面那個式子可以得出k只有為奇數時,後面那個才能是k的整數倍;   

然後我們再將目光聚焦到前面的n上,當n為奇數時不用想肯定是可以取2的(前面的借鑑作用),所以當n為偶數時,如果他能被k整除,就是對的,說明n不能是2的指數冪,再次證明了我們剛剛的猜想是正確的;所以我們做以下操作:

  當n能被2整除時,就除以2直到n變成奇數,也就是我們這裡要的k就行了;

 

但是問題真正解決了嗎? 如果當前的dp[k] > n本身呢?說明我們k取大了對吧,然後我們剛剛的操作相當於把n拆成了奇數k和2的指數冪x,那我們就想取x,又會發現後半部分除以x後變成(1+x)/ 2,因為x為偶數,所以這樣是不行的,所以我們就想這個2這麼礙眼,如果我乾脆取2x,會得到(k - 1 - 2x ) / 2,我們要證明這個能整除且為非負數,因為k為奇數,1+2x為奇數,所以奇數減奇數肯定是偶數,所以一定成立,但是k一定要大於等於2x+1,就一定滿足題幹,所以我們發現2x  < k時,dp[k] > n ,2x >k時,dp[k]一定剛好滿足條件;

所以對於k>1時,我們就取最後的ans = min(k,2x);

總結:這道題很多主要是數學思想上的,很多地方需要用到猜想的方法,然後對於應付大資料的數我們就要這樣類似的處理,然後我們可能一開始得不出正確答案,但是通過調整之後一定能夠得出我們所要的ans;

 

程式碼:

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;

int main()
{
int t;
cin >> t;
while(t--){
LL n;
cin >> n;
LL k = 1;
while(n%2 == 0){
n/=2;
k*=2;
}
if(n == 1) cout << -1 <<'\n';
else cout << min(n,2*k) << '\n';
}
return 0;
}