題目連結:等差數列計數
題目描述
首先根據題目要求,給定一個等差數列的首項 \(t_1\) 和這個等差數列的末項 \(t_n\),問符合這個形式的等差數列的數量。
例如,對於第一個 \(\mathtt{Testcase}\),當 \(t_1\) 為 \(1\),\(t_n\) 為 \(9\) 時,可行的等差數列方案數有四個,分別為以下所示:
- \(S = \{1, 2, 3, 4, 5, 6, 7, 8, 9\}\)
- \(S = \{1, 3, 5, 7, 9\}\)
- \(S = \{1, 5, 9\}\)
- \(S = \{1, 9\}\)
思路分析
我們知道,等差數列的公差數量就是這個等差數列的可行方案數,即有多少種不同的公差方案,就可以構造出多少種不同的等差數列。因此透過分析題意,我們可以將問題更細緻地轉換為 已知等差數列的首項和尾項,求出這個等差數列公差的可行方案數。所以對於本題而言,我們只需要求出有多少個公差就可以了。
顯然本題的就引刃而解了,我們只需要求出這個等差數列首項和末項的差的絕對值,即 \(diff = \lvert t_n - t_1 \rvert\)。然後我們只需要求出 \(diff\) 的因數個數即可。\(diff\) 的因數就是可行的因數方案(詳細證明過程見下文)。
例如,當 \(t_1\) 為 \(1\),\(t_n\) 為 \(9\) 時,\(diff = \lvert 9 - 1 \rvert = 8\)。\(8\) 的因數有四個,分別是 \(\{1, 2, 4, 8\}\),因此當這個等差數列首項為 \(1\),末項為 \(9\) 時,可行的等差數列方案應為四個。
結論證明
透過等差數列公式,我們可以將等差數列的首項和末項透過公式聯立起來,得到 \(t_n = t_1 + (n-1)\times d\),其中 \(n\) 表示等差數列的項數,\(d\) 表示等差數列的公差。
由於我們想要求解所有的因數個數,因此我們將透過移項的操作將 \(d\) 放到等式左邊,將其餘的量都放到等式右邊。得到:
由於等差數列的性質,\(n - 1\) 必須為非負整數(等差數列的長度不能 \(0\))。或者根據等差數列的另一個性質,如果首項和末項的差為負數,那麼公差也必須為負數,反之亦然。因此也可以推匯出 \(n - 1\) 必須是非負整數。
為了方便起見,我們將 \(n-1\) 看作為一個整體,另 \(\Delta t = n - 1\)。將該整體代入方程後即可得到 \(d = \frac{t_n - t_1}{\Delta t}\)。為了使 \(d\) 是一個整數,因此 \(t_n - t_1\) 必須是 \(\Delta t\) 的倍數。因此我們只需要透過列舉上述方程,計算 \(t_n - t_1\) 所有的因數數量就可以得到本問題的解。
AC 程式碼
以下是本題的 AC 程式碼。需要注意的是,由於本題的資料量比較大,因此在判斷因數的過程中需要最佳化演算法(與判斷質數類似),這樣子演算法就可以在 \(O(\sqrt{n})\) 的時間內完成列舉,不至於超時:
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int t, a, b;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> t;
while(t--){
cin >> a >> b;
int change = abs(b - a), cnt = 0;
for (int i=1; i*i<=change; i++){
if (change % i == 0){
if (i*i != change) cnt += 2;
else cnt += 1;
}
}
cout << cnt << endl;
}
return 0;
}