Distance Tree 全网最丑做法

本文探讨了一种图论问题,涉及找到从根节点出发,经过新加入边的最大路径长度。通过建立不等式,作者推导出一个求解最大路径长度的公式,并利用二分查找来优化计算过程。文章还提供了C++实现的参考代码。

1.前言

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

2.题解

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

假设我们当前连边的长度为 xxx,连接到的点距离 111yyy,最短路经过新加入的边的点的集合为 sssidxiidx_iidxi 表示 iii 向上的第一个在 lll 上的点距离 111 的长度,hih_ihi 表示 iii 到向上的第一个在 lll 上的点的距离 , midmidmid 表示答案。

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

在这里插入图片描述

我们可以列出两个不等式
∣y−idxi∣+hi+x≤mid(i∈S)idxi+hi≤mid(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∈Si \in \mathbb{S}iS 的不等式进行化简。

idxi−(mid−x−hi)≤y≤idxi+(mid−x−hi) idx_i - (mid - x - h_i) \leq y \leq idx_i+ (mid - x - h_i) idxi(midxhi)yidxi+(midxhi)

我们的要求就是

∃y,idxi−(mid−x−hi)≤y≤idxi+(mid−x−hi)(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⁡(idxi+(mid−x−hi)(i∈S))≥max⁡(idxi−(mid−x−hi))(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))

通过这个,我们反解出 midmidmid

mid≥⌈2∗x+max⁡(idxi+hi)−min⁡(idxi−hi)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)

那么,我们可以得出 midmidmid 的表达式

max⁡(⌈2∗x+max⁡(idxi+hi)−min⁡(idxi−hi)2⌉(i∈S),idxi+hi(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))

按照 idxi+hiidx_i +h_iidxi+hi 排序,贪心容易知道,选择的 S\mathbb{S}S 集合在排序后的序列上一定是连续的,且左端点为 111,记 S\mathbb{S}S 的右端点为 rrr

发现左边的式子随着 rrr 的变大而变小。如果记左,右边的式子分别为函数 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)=0g(0) = 0, g (n + 1) \neq 0, f(0) \neq 0, f (n + 1) = 0g(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;
}
### 关于树结构间的编辑距离计算 #### 定义与概念 树编辑距离(Tree Edit Distance, TED)衡量两棵树之间差异的程度。该度量定义为将一棵树转换成另一棵树所需的最少操作次数,这些基本操作通常包括节点的插入、删除以及替换。 #### 计算方法概述 为了有效地计算TED,常用的方法是动态规划。具体来说: - **初始化阶段**:创建一个二维矩阵D,其中`D[i][j]`表示由源树根到第i个孩子构成的子树和目标树相应部分至第j个孩子的最小代价。 - **递推关系构建**:对于任意一对节点u和v,在考虑它们各自的孩子列表时应用如下规则来更新矩阵条目: - 如果两个节点相同,则不需要任何改变; - 否则,尝试通过三种方式之一减少两者间差距——即移除其中一个节点、添加新节点或更改现有节点标签,并选取成本最低者作为最终结果; - **边界条件设定**:当处理到达叶子结点的情况时,如果一方还有剩余未匹配的部分,则按照实际需求执行必要的增删动作直至完全同步为止。 #### 实现示例 下面给出Python语言下的简单实现版本[^1]: ```python def tree_edit_distance(tree1, tree2): m = len(tree1) n = len(tree2) # Initialize cost matrix dist_matrix = [[0 for _ in range(n + 1)] for __ in range(m + 1)] # Fill the first row and column for i in range(1, m + 1): dist_matrix[i][0] = i for j in range(1, n + 1): dist_matrix[0][j] = j # Compute costs using dynamic programming approach for i in range(1, m + 1): for j in range(1, n + 1): if tree1[i-1].label == tree2[j-1].label: substitution_cost = 0 else: substitution_cost = 1 dist_matrix[i][j] = min( dist_matrix[i-1][j] + 1, # Deletion from t1 dist_matrix[i][j-1] + 1, # Insertion into t1 dist_matrix[i-1][j-1] + substitution_cost # Substitution ) return dist_matrix[m][n] class TreeNode: def __init__(self, label=None, children=None): self.label = label self.children = children if children is not None else [] # Example usage if __name__ == "__main__": root_a = TreeNode('A', [ TreeNode('B'), TreeNode('C') ]) root_b = TreeNode('A', [ TreeNode('B'), TreeNode('D') ]) print(f"The Tree Edit Distance between two trees is {tree_edit_distance([root_a], [root_b])}") ``` 此代码片段展示了如何利用动态规划技术求解给定二叉树之间的最短路径长度。需要注意的是这里简化了输入形式以便理解逻辑流程,真实应用场景下还需要进一步完善数据结构设计以适应更复杂的多分支情况。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值