嘘~ 正在从服务器偷取页面 . . .

CF1632E2 Distance Tree (hard version) 题解


CF1632E2 Distance Tree (hard version) 题解

题目链接:Distance Tree (hard version)

题意

本题与 CF1632E1 的唯一区别在于 \(n\) 的数据范围。

给定一个包含 \(n\) 个节点的树,每条边的长度为 \(1\)。给出如下定义:

  • 定义 \(d(v)\) 为从节点 \(1\) 到节点 \(v\) 的距离。
  • 定义 \(f(x)\) 为在任意两个点 \(a,b\) 之间添加一个长度为 \(x\) 的边后,\(\max\limits_{1\le v\le n}d(v)\) 的最小值。

现在,对于所有的 \(1\le x\le n\),求出 \(f(x)\) 的值。

数据范围

\(t\) 组数据,\(1\le t\le 10^4,~2\le \sum n\leq 3\times 10^5\)

显然,以 \(1\) 为起点连边一定不劣。

设我们已经求出了 \(f(x)=k\) ,那么我们只需要考察 \(d(x) > k\) 的点即可,因为 \(d(x)\le k\)\(x\) 连边也没意义。

\(D_k\) 为所有 \(d(x) > k\) 的节点两两之间的最大距离,则有 \(\left\lceil\frac{D_k}{2}\right\rceil+x \le k\)

这个不等式的可以看作,将整张图中最长的链(直径)的中点与 \(1\) 连边。

注意到当 \(k\) 增大时,\(d(x) > k\) 的节点数会减少,这样 \(D_k\) 就是单调减的,已经可以二分出来了。

\(x\) 增大的过程中会使 \(k\) 单调不减,因此我们只需要预处理出 \(D_k\) 就可以线性计算答案了。

时间复杂度 \(\mathcal{O}(n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define pb push_back
#define N ((int)(3e5 + 15))

vector<int> vec[N]; int dep[N], mx[N];
int dfs(int u, int fa)
{
    dep[u] = dep[fa] + 1; int mx1 = dep[u], mx2 = dep[u];
    for(int v : vec[u]) if(v != fa)
    {
        int k = dfs(v, u);
        if(k > mx1) { mx2 = mx1, mx1 = k; } else up(mx2, k);
    }
    int p = min(mx1, mx2) - 1;
    if(~p) up(mx[p], mx1 + mx2 - 2 * dep[u] + 1);
    return mx1;
}
void solve()
{
    int n; cin >> n; dep[0] = -1;
    rep(i, 0, n + 5) { vec[i].clear(), mx[i] = 0; }
    for(int i = 1, u, v; i < n; i++) { cin >> u >> v; vec[u].pb(v); vec[v].pb(u); }
    int k = dfs(1, 0); Rep(i, k - 1, 0) up(mx[i], mx[i + 1]);
    int res = 0;
    rep(i, 1, n)
    {
        while(res < k && mx[res] / 2 + i > res) ++res;
        cout << res << " \n"[i == n];
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int qwq; cin >> qwq; while(qwq--) solve();
    return 0;
}

双倍经验:CF1632E1 Distance Tree (easy version)


参考文献

[1] https://www.luogu.com.cn/article/rhy4zedk

[2] https://www.luogu.com.cn/article/4xrn95l0


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录