Good Tree (牛客多校4 F)

进入博客阅读体验更佳: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(1x1018),如果一棵树中存在两个节点使得 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=2n12n1,接下来思考对这条链上加点,具体对以下这条链进行操作:

在这里插入图片描述

首先选取节点 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(u1u6),其中 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 1516以下才能保证精度。

参考代码

#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;
}
  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值