Sgu507 - Treediff 树上启发式合并

Sgu507 - Treediff

树上启发式合并学习笔记

题意

N N N个点以1为根的树,其中 M M M个点为叶节点,只有叶节点有权值 a i a_i ai
现在求每一个非叶节点的子树中,任意两个叶节点的权值差的绝对值最小是多少(如果不存在输出 2 31 − 1 2^{31}-1 2311

分析

我们需要维护的信息是,子树中每一个叶节点的权值
当我们新加入一个叶节点的值 v a l val val的时候,我们只需要找小于 v a l val val中最大的值,和大于 v a l val val中的最小值即可
那么我们可以用 m a p map map或是 m u l t i s e t multiset multiset来存储已经有的叶节点权值
只需要找到 m a p map map或是 m u l t i s e t multiset multiset v a l val val的地址,往前一个就是比 v a l val val小的最大值,往后一个就是比 v a l val val大的最小值,这样每次加入一个点都更新答案即可

这里我用 m a p map map来实现

代码

#include 
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MAX = 5e4 + 10;

int N, M;
int a[MAX];
char s[MAX];
vector<int> g[MAX];

int siz[MAX], son[MAX];
void dfs(int u, int fa) {
    siz[u] = 1;
    for (auto &v: g[u])
        if (v != fa) {
            dfs(v, u);
            siz[u] += siz[v];
            if (!son[u] || siz[v] > siz[son[u]])
                son[u] = v;
        }
}

int vis[MAX], ans[MAX], mn = INT_MAX;//INT_MAX就是2^31 - 1
map<int, int> mp;
map<int, int>::iterator it, t;
void upd(int u, int fa, int k) {
    if (u >= N - M + 1 && k == 1) {
        mp[a[u]]++;
        if (mp[a[u]] == 1) {//如果当前这个点只有一个, 那就往前往后找
            it = mp.find(a[u]), t = it;
            if (it != mp.begin()) {//往前找, 并且不是第一个的时候
                it--;//前一个地址
                mn = min(mn, a[u] - (*it).first);
            }
            it = t;
            t = mp.end(); t--;//map.end()-1才是map中最后一个元素的地址
            if (it != t) {//同理
                it++;
                mn = min(mn, (*it).first - a[u]);
            }
        }
        else mn = 0;//如果这个权值不止一个,那么差值可以是0了
    }
    for (auto &v: g[u])
        if (v != fa && !vis[v]) upd(v, u, k);
}

void dsu(int u, int fa, int keep) {
    for (auto &v: g[u])
        if (v != fa && v != son[u]) dsu(v, u, 0);
    if (son[u]) dsu(son[u], u, 1), vis[son[u]] = 1;
    upd(u, fa, 1);
    ans[u] = mn;
    if (son[u]) vis[son[u]] = 0;
    if (!keep) mp.clear(), mn = INT_MAX;
}

int main() {
    scanf("%d%d", &N, &M);
    for (int i = 2; i <= N; i++) {
        int u; scanf("%d", &u);
        g[u].push_back(i); g[i].push_back(u);
    }
    for (int i = N - M + 1; i <= N; i++) scanf("%d", &a[i]);
    dfs(1, 0);
    dsu(1, 0, 0);
    for (int i = 1; i <= N - M; i++) printf("%d%s", ans[i], i == N - M ? "\n" : " ");

    return 0;
}

你可能感兴趣的:(树和森林,#,树上启发式合并,算法)