Distance Tree 全网最丑做法

1.前言

为什么我总是一些正常人想不到的思路……

2.题解

首先有一个很显然的结论,连接的两个点一个为根节点,一个为以一为链顶的最长链上,记这个最长链的点集合为 l l l

假设我们当前连边的长度为 x x x,连接到的点距离 1 1 1 y y y,最短路经过新加入的边的点的集合为 s s s i d x i idx_i idxi 表示 i i i 向上的第一个在 l l l 上的点距离 1 1 1 的长度, h i h_i hi 表示 i i i 到向上的第一个在 l l l 上的点的距离 , m i d mid mid 表示答案。

用一张图形象的表示 ↓ \downarrow

在这里插入图片描述

我们可以列出两个不等式
∣ y − i d x i ∣ + h i + x ≤ m i d ( i ∈ S ) i d x i + h i ≤ m i d ( i ∉ S ) \begin{aligned} \mid y - idx_i \mid + h_i + x \leq mid (i \in \mathbb{S}) \\ idx_i+h_i \leq mid (i \notin \mathbb{S}) \end{aligned} yidxi+hi+xmid(iS)idxi+himid(i/S)

第二个不等式已经足够简洁,我们对第一个 i ∈ S i \in \mathbb{S} iS 的不等式进行化简。

i d x i − ( m i d − x − h i ) ≤ y ≤ i d x i + ( m i d − x − h i ) idx_i - (mid - x - h_i) \leq y \leq idx_i+ (mid - x - h_i) idxi(midxhi)yidxi+(midxhi)

我们的要求就是

∃ y , i d x i − ( m i d − x − h i ) ≤ y ≤ i d x i + ( m i d − x − h i ) ( i ∈ S ) \exists y,idx_i - (mid - x - h_i) \leq y \leq idx_i+ (mid - x - h_i)(i \in \mathbb{S}) y,idxi(midxhi)yidxi+(midxhi)(iS)

什么情况满足呢?可以把它想成多个区间求交集后不为空集,即

min ⁡ ( i d x i + ( m i d − x − h i ) ( i ∈ S ) ) ≥ max ⁡ ( i d x i − ( m i d − x − h i ) ) ( i ∈ S ) ) \min (idx_i+ (mid - x - h_i)(i \in \mathbb{S})) \geq \max (idx_i - (mid - x - h_i))(i \in \mathbb{S})) min(idxi+(midxhi)(iS))max(idxi(midxhi))(iS))

通过这个,我们反解出 m i d mid mid

m i d ≥ ⌈ 2 ∗ x + max ⁡ ( i d x i + h i ) − min ⁡ ( i d x i − h i ) 2 ⌉ ( i ∈ S ) mid \geq \lceil \frac{2*x + \max (idx_i + h_i) - \min (idx_i - h_i)}{2} \rceil (i \in \mathbb{S}) mid22x+max(idxi+hi)min(idxihi)(iS)

那么,我们可以得出 m i d mid mid 的表达式

max ⁡ ( ⌈ 2 ∗ x + max ⁡ ( i d x i + h i ) − min ⁡ ( i d x i − h i ) 2 ⌉ ( i ∈ S ) , i d x i + h i ( i ∉ S ) ) \max (\lceil \frac{2*x + \max (idx_i + h_i) - \min (idx_i - h_i)}{2} \rceil (i \in \mathbb{S}), idx_i+h_i(i \notin \mathbb{S})) max(⌈22x+max(idxi+hi)min(idxihi)(iS),idxi+hi(i/S))

按照 i d x i + h i idx_i +h_i idxi+hi 排序,贪心容易知道,选择的 S \mathbb{S} S 集合在排序后的序列上一定是连续的,且左端点为 1 1 1,记 S \mathbb{S} S 的右端点为 r r r

发现左边的式子随着 r r r 的变大而变小。如果记左,右边的式子分别为函数 f ( r ) , g ( r ) f (r), g (r) f(r),g(r),则大概有这样一个函数图像 ↓ \downarrow

在这里插入图片描述
这个最大值函数的最小值即为 f ( x ) f(x) f(x) g ( x ) g(x) g(x) 的交点 (因为 g ( 0 ) = 0 , g ( n + 1 ) ≠ 0 , f ( 0 ) ≠ 0 , f ( n + 1 ) = 0 g(0) = 0, g (n + 1) \neq 0, f(0) \neq 0, f (n + 1) = 0 g(0)=0,g(n+1)=0,f(0)=0,f(n+1)=0,以及 f ( x ) f(x) f(x) 为单调不递增函数, g ( x ) g(x) g(x) 为单调不递减函数,可以证明他们必有交点,且交点的函数值相等),这是一个经典问题,二分 max ⁡ ( x ) ( f ( x ) ≥ g ( x ) ) \max (x) (f(x) \geq g(x)) max(x)(f(x)g(x)) 即可。

参考代码

#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define int long long
#define PII pair <int, int>
#define ULL unsigned long long
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); i++)
#define per(i,j,k) for (int i = (j); i >= (k); i--)

template <typename T>
void read (T &x) {
    x = 0; T f = 1; 
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... Arg) {
    read (x), read (Arg...);
}
const int MaxPrint = 1000;
int Poi_For_Print, Tmp_For_Print[MaxPrint + 5];
template <typename T>
void write (T x) {
	if (x == 0) {
		putchar ('0');
		return;
	}
    bool flag = (x < 0 ? 1 : 0);
    x = (x < 0 ? -x : x);
    while (x) Tmp_For_Print[++Poi_For_Print] = x % 10, x /= 10;
    if (flag) putchar ('-');
    while (Poi_For_Print) putchar (Tmp_For_Print[Poi_For_Print--] + '0');
}
template <typename T, typename... Args>
void write (T x, Args... Arg) {
    write (x); putchar (' '); write (Arg...);
}
template <typename T, typename... Args>
void print (T x, char ch) {
    write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }

const int Maxn = 3 * 1e5;
const int Inf = 0x3f3f3f3f3f3f3f;

int t, n;

vector <int> g[Maxn + 5];
void add (int x, int y) {
    g[x].push_back (y);
}

int dp[Maxn + 5], id[Maxn + 5], fa[Maxn + 5];
void Tree (int u, int _fa) {
    for (auto v : g[u]) {
        if (v == _fa) continue;
        fa[v] = u;
        Tree (v, u);
        if (dp[v] + 1 > dp[u]) {
            dp[u] = dp[v] + 1;
            id[u] = v;
        }
    }
}
int cnt, dist[Maxn + 5], _min[Maxn + 5], _max[Maxn + 5];
bool vis[Maxn + 5];
struct Node {
    int h, idx;
} a[Maxn + 5];
bool cmp (Node x, Node y) {
    return x.h + x.idx < y.h + y.idx;
}
int Query1 (int mid) {
	if (mid < 0 || mid > cnt) return Inf;
	if (mid == 0) return 0;
    return a[mid].idx + a[mid].h;
}
int Query2 (int mid, int x) {
	if (mid < 1 || mid > cnt + 1) return Inf;
	if (mid == cnt + 1) return 0;
    return (2 * x + _max[mid] - _min[mid] + 1) / 2;
}

signed main () {
//    freopen ("C:\\Users\\Administrator\\Desktop\\lihan\\1.in", "r", stdin);
    // freopen ("C:\\Users\\Administrator\\Desktop\\lihan\\1.out", "w", stdout);

    read (t);
    while (t--) {
        read (n);
        rep (i, 1, cnt) a[i].h = a[i].idx = 0; cnt = 0;
        rep (i, 1, n) g[i].clear (), dp[i] = 0, dist[i] = 0, vis[i] = 0;
        
        rep (i, 1, n - 1) {
            int x, y; read (x, y);
            add (x, y), add (y, x);
        }
        Tree (1, -1);
        int u, v, Now = 0; u = 1;
        do {
            v = u;
            u = id[u];
            vis[v] = 1;
            dist[v] = Now++;
        } while (g[v].size () != 1 || v == 1);
        rep (i, 2, n) {
        	if (vis[i]) {
        		cnt++;
        		a[cnt].h = 0;
        		a[cnt].idx = dist[i];
        		continue;
			}
            if (g[i].size () == 1) {
                cnt++;
                v = i;
                while (!vis[v]) {
                    a[cnt].h++;
                    v = fa[v];
                }
                a[cnt].idx = dist[v];
            }
        }
        sort (a + 1, a + 1 + cnt, cmp);
        
        _max[cnt + 1] = -Inf; _min[cnt + 1] = Inf;
        per (i, cnt, 1) {
            _max[i] = Max (_max[i + 1], a[i].idx + a[i].h);
            _min[i] = Min (_min[i + 1], a[i].idx - a[i].h);
        }
        rep (x, 1, n) {
            int l = -1, r = cnt + 2;
            while (l + 1 < r) {
                int mid = (l + r) >> 1;
                if (Query1 (mid) <= Query2 (mid + 1, x)) l = mid;
                else r = mid;
            }
            print (Min (Max (Query1 (l), Query2 (l + 1, x)), Max (Query1 (l + 1), Query2 (l + 2, x))), ' ');
        }
        putchar ('\n');
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值