Codeforces Round 1033 (Div. 2) and CodeNite 2025 C. Divine Tree
C. Divine Tree
思路: 构造题,每个人方法都不一定一样。我的方法思路如下:-
首先确定上下界:下届为 n,即全 1;上届为前 n 项和,等效于以 n 为根,连接其余点;
-
然后可以确定根结点一定是最大的那个 d(v),这个也好想。比如
\[13 = 1 + 2 + 3 + 4 + 3 \]那就是根节点 4 连了 {1,2,3},然后 5 和 3 连。换作其他比 4 小的点作根,4 不可能出现;
-
若任意一个上下界范围内的数 x 有解,那么x+1也一定有解。证明:
\[x = 1\cdot cnt_1 + 2\cdot cnt_2 + 3\cdot cnt_3 + \dots + n\cdot cnt_n \]由于没到上届,那么一定有一个 i,使得 $$cnt_i < cnt_{i-1},$$那么只需要
\[cnt_i+1,\quad cnt_{i-1}-1 \]其他项不变,就可以得到 x+1 了,如
\[14 = 1 + 2 + 3 + 4 + 4,就是把其中一个 3 换成 4,那么总和就多了 1; \] -
具体构造时,对每个位置 i 从 1 遍历到 n:
假设后续全放 1,则剩余量为r = m - (n - i + 1),如果 \(1 \le r+1 \le n\),就把这个位置直接放 \(r+1\),后续全 1,否则这个位置放 i,并做 \(m -= i\)。
将每个位置的值记录到 \(a[i]\); -
完成后,根 rt 之前的所有点都和 rt 相连; rt 之后的位置 i 则和 \(a[i]\) 相连,即可得到所需树。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 200;
ll a[maxn];
inline ll read() {
ll f = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x*10 + (ch - '0'); ch = getchar(); }
return x * f;
}
void sol() {
ll n = read(), k = read();
ll l = n, r = (n+1)*n/2;
if (k < l || k > r) {
cout << -1 << "\n";
return;
}
ll rt = 1;
bool flag = false;
for (int i = 1; i <= n; i++) {
if (flag) {
a[i] = 1;
continue;
}
ll x = n - i + 1;
if (k - x >= 0 && k - x <= n - 1 && 1 + (k - x) <= i) {
rt = max(rt, 1LL + (k - x));
a[i] = 1 + (k - x);
flag = true;
} else {
rt = i;
a[i] = i;
k -= i;
}
}
cout << rt << "\n";
for (int i = 1; i < rt; i++) {
cout << rt << " " << i << "\n";
}
for (int i = rt + 1; i <= n; i++) {
if (a[i] == 1) cout << i << " 1\n";
else cout << i << " " << a[i] << "\n";
}
}
int main() {
int T; cin >> T;
while (T--) sol();
return 0;
}

浙公网安备 33010602011771号