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}\)。进而可以拆成两个条件:
- \(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 个条件,显然 \(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;
}