1. 程式人生 > 其它 >CF1656D K-good

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​\)

個互不相同的正整數”,那麼它們至少是 \(1, 2, \dots, k​\)。因為是在 \(\bmod k​\) 意義下考慮這 \(k​\) 個正整數,所以我們還可以給其中任意一個數加上任意個 \(k​\)。所以,題目的要求等價於 \(n​\) 可以被寫成這種形式:\(n = 1 + 2 + \dots + k + k \cdot m = \frac{k(k + 1)}{2} + k\cdot m​\),其中 \(m​\) 是任意非負整數。

分類討論,先考慮 \(k\) 是偶數的情況。那麼相當於 \(n = \frac{k}{2} \cdot k + \frac{k}{2} + k\cdot m = k\left(\frac{k}{2} + m\right) + \frac{k}{2}\)

。進而可以拆成兩個條件:

  1. \(n \bmod k = \frac{k}{2}\)
  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 個條件,顯然 \(k\) 要越小越好。所以可以令 \(k_1 = 2^{\nu_2(n) + 1}\)

此時檢查第 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;
}