CF1656D K-good
CF1656D K-good
題目大意
對於一個正整數 \(n\) 和一個正整數 \(k\),稱 \(n\) 是 \(k\)-good 的當且僅當 \(n\) 可以被寫成 \(k\) 個正整數之和,且這 \(k\) 個正整數除以 \(k\) 的餘數互不相同。
\(T\) 次詢問,每次給你一個 \(n\),請你求出任意一個 \(\geq 2\) 的 \(k\) 使得 \(n\) 是 \(k\)-good 的,或告知無解。
資料範圍:\(1\leq T\leq 10^5\),\(1\leq n\leq 10^{18}\)。
本題題解
考慮 \(n\) 是 \(k\)-good 的條件。因為題目要求“\(k\)
分類討論,先考慮 \(k\) 是偶數的情況。那麼相當於 \(n = \frac{k}{2} \cdot k + \frac{k}{2} + k\cdot m = k\left(\frac{k}{2} + m\right) + \frac{k}{2}\)
- \(n \bmod k = \frac{k}{2}\)
- \(\frac{n - \frac{k}{2}}{k} \geq \frac{k}{2}\),即 \(n \geq \frac{k}{2}\cdot (k + 1)\)
先考慮第 1 個條件。它相當於:\(k\) 是 \(2n\) 的約數,且不是 \(n\) 的約數。那麼顯然這就和 \(n\) 分解質因數後“\(2\)”的次數息息相關了。將 \(n\) 中 \(2\) 的最高次數記為 \(\nu_2(n)\)。那麼第 1 個條件等價於:\(k\) 必須是 \(2^{\nu_2(n) + 1}\) 乘以一個 \(n\)
此時檢查第 2 個條件,若 \(\frac{k_1}{2} \cdot (k_1 + 1) \leq n\),那麼 \(k_1\) 就是答案。否則說明偶數的 \(k\) 無解,只能考慮奇數的情況。
一個巧妙的想法是,令 \(k_2 = \frac{2n}{k_1}\),顯然,\(k_2\) 是一個奇數。下面我們將證明,只要 \(k_2 \neq 1\)(題目要求 \(k\geq 2\)),那麼 \(k_2\) 一定是滿足條件的答案。因為 \(\frac{k_1}{2} \cdot (k_1 + 1) > n\),所以 \(\frac{2n}{k_1} < k_1 + 1\),即 \(k_2 < k_1 + 1\)。又因為 \(k_2\) 是奇數,所以 \(k_2 \leq k_1 - 1\)。所以 \(\frac{k_2(k_2 + 1)}{2} \leq \frac{k_2\cdot k_1}{2} = n\)。又因為 \(n\) 是 \(k_2\) 的倍數,所以 \(n - \frac{k_2(k_2 + 1)}{2} = n - k_2\cdot\frac{k_2 + 1}{2}\) 也一定是 \(k_2\) 的倍數(即 \(k_2 \cdot m\) 的形式)。綜上所述,\(k_2\) 滿足所有要求。
另外,若 \(k_2 = 1\),則一定無解。因為前面已經說過,\(k\) 只能是奇數。同時,\(n\) 顯然又必須是 \(k\) 的倍數。而 \(k_2 = 1\) 意味著 \(n\) 是 \(2\) 的整數次冪,它沒有其它奇約數。所以此時輸出 \(-1\) 即可。
單次詢問時間複雜度 \(\mathcal{O}(\log_2 n)\),總時間複雜度 \(\mathcal{O}(T\cdot \log_2n)\)。
參考程式碼
// problem: CF1656D
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
void solve_case() {
ll n;
cin >> n;
int v = 1;
while (n % (1LL << v) == 0)
++v;
ll k1 = (1LL << v);
if (n * 2 / k1 >= k1 + 1) { // 等價於 (k1 * (k1 + 1) / 2 <= n),這樣寫防止爆 long long
cout << k1 << endl;
return;
}
int k2 = n * 2 / k1;
if (k2 == 1) {
cout << -1 << endl;
return;
}
cout << k2 << endl;
}
int main() {
int T;
cin >> T;
while (T--) {
solve_case();
}
return 0;
}