进入博客阅读体验更佳:Good Tree (牛客多校4 F) | 付诺の小站 (funuo0728.github.io)
Floor Tiles
题目大意
给定一棵所有边权为 1 1 1的树,定义 f ( u ) = ∑ v d i s ( u , v ) f(u) = \sum_{v} dis(u,v) f(u)=∑vdis(u,v), v v v表示树中所有节点, d i s ( u , v ) dis(u, v) dis(u,v)是指节点 u u u到节点 v v v之间的简单路径的长度。
给出一个整数 x ( 1 ≤ x ≤ 1 0 18 ) x(1 \le x \le 10^{18}) x(1≤x≤1018),如果一棵树中存在两个节点使得 f ( u ) − f ( v ) = x f(u) - f(v) = x f(u)−f(v)=x,则称这棵树为“好树”,求构造出一棵“好树”所需的最小节点数。
解题思路
首先考虑 n n n个节点所能构造出的 f f f的最大差值,很明显,形成一条链的时候差值最大,可以选取端点和最中间的点,可以得到最大差值 w = ⌊ n − 1 2 ⌋ ⋅ ⌈ n − 1 2 ⌉ w = \lfloor\frac{n - 1}{2}\rfloor \cdot \lceil\frac{n - 1}{2}\rceil w=⌊2n−1⌋⋅⌈2n−1⌉,接下来思考对这条链上加点,具体对以下这条链进行操作:
首先选取节点 1 1 1和节点 6 6 6,此时 w = 5 × 5 = 25 w = 5 \times 5 = 25 w=5×5=25,然后我们可以在 6 6 6的右边多连一个点,或者在 1 1 1的左边多连一个点,或者在节点 1 1 1到节点 6 6 6的之间的简单路径上加点。然而在 6 6 6的右边和在 1 1 1的左边加点是等效的,因为,当在节点 1 1 1到节点 6 6 6的简单路径上没有多余的分叉的话,这两个点对应的 f f f差值 w = d ( ∣ u 1 − u 6 ∣ ) w = d(\vert u_1 - u_6\vert) w=d(∣u1−u6∣),其中 d d d是指这两个点之间的简单路径长度, u i u_i ui是指除去在简单路径上与 i i i相连的点的个数。所以这两者是等效的。
接下来对路径上加点和非路径上加点进行讨论,首先是在路径上加点:
如果我们在节点 5 5 5上向外连点,则 w w w变为 25 + 3 = 28 25 + 3 = 28 25+3=28,同理如果在节点 4 4 4上向外连点,则 w w w变为 25 + 1 = 26 25 + 1 = 26 25+1=26;
然后是在非路径上加点,整个图的 w ’ = 5 × 6 = 30 w’ = 5 \times 6 = 30 w’=5×6=30。
这个时候发现,在区间 [ 26 , 30 ] [26, 30] [26,30]上,并且整棵树的大小为 n + 1 n + 1 n+1时, w w w取不到 27 27 27和 29 29 29,又由于大小为 n n n时 w w w最大为 25 25 25,所以只能考虑 n + 2 n+2 n+2及以上的情况,可以将选取的节点 6 6 6往后挪一位挪到节点 7 7 7,这个时候 w = 24 w = 24 w=24,经过手玩发现,在路径上加 2 2 2个点可以将剩下的情况(即 27 27 27和 29 29 29)取到。
然后继续在节点为奇数个,且中间点到端点的简单路径长度为偶数时,与上面的分析类似。
最后再讨论节点为偶数个的情况,发现,加一个点可以将区间 [ w + 1 , w ′ ] [w + 1, w'] [w+1,w′]上的值全部取到。
根据数学归纳法以及整合以上信息,最终可以得到一个这样的做法:先求出满足 f f f最大差值小于等于 x x x的最大 n n n,再对 n n n分奇偶讨论,对 n n n为奇数的时候再对中间点与端点距离的奇偶进行讨论,这里可以整合为, x x x为偶数时,答案为 n + 1 n + 1 n+1,否则为 n + 2 n + 2 n+2。不太懂的可以看一下这份提交记录,对照一下就懂了。
注意求 n n n时不能直接用 s q r t sqrt sqrt,因为 d o u b l e double double只有在有效数字在 15 − 16 15 - 16 15−16以下才能保证精度。
参考代码
#include <bits/stdc++.h>
#define maxn 200100
#define int long long
using namespace std;
const double eps = 1e-8;
int a[maxn];
int sqr(int x){
int l = 1, r = 1e9 + 10;
while(l <= r) {
int mid = l + r >> 1;
if(mid * mid <= x) l = mid + 1;
else r = mid - 1;
}
return r;
}
void solve(){
int x; cin >> x;
int t1 = sqr(x), t2 = t1;
if(t1 * (t1 + 1) <= x) t2++;
int n = t1 + t2 + 1;
if(t1 * t2 == x) cout << n << '\n';
else if(t1 != t2) cout << n + 1 << '\n';
else if(t1 == t2) {
if(x % 2 == 0) cout << n + 1 << '\n';
else cout << n + 2 << '\n';
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t = 1; cin >> t;
while (t--) {
solve();
}
return 0;
}