dfs序+线段树,CF 838B - Diverging Directions

目录

一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

二、解题报告

1、思路分析

2、复杂度

3、代码详解


一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

838B - Diverging Directions


二、解题报告

1、思路分析

注意这是一棵有向树

添加了“回跳边”后

对于任意查询<u, v>

如果 v 在 u 子树内,显然从u直接走到v是最短的,答案为 d[v] - d[u]

否则,路径应该是 从 u 走到 子树内某节点x,从x走"回跳边"到根,再从根走到v

上述路径长度为:d[x] - d[u] + up[x] + d[v]

即,答案为 min{up[x] + d[x]} + d[v] - d[u]

发现我们似乎只需维护 min { up[x] + d[x] }

那么我们发现操作树边,相当于是修改一棵子树的 {up[x] + d[x]}

操作回跳边,则是单点修改up[x] + d[x]

所以,我们只需用线段树维护 dfs序列上的 {up[x] + d[x]}即可

2、复杂度

时间复杂度: O(qlogn)空间复杂度:O(nlogn)

3、代码详解

 ​
#include <bits/stdc++.h>

// #define DEBUG

using i64 = long long;
using u32 = unsigned int;
using u64 = unsigned long long;

constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
constexpr int P = 1E9 + 7;

template <class Info, class Tag>
struct LazySegmentTree {
    const int n;
    std::vector<Info> info;
    std::vector<Tag> tag;
    LazySegmentTree(int _n) : n(_n), info(2 << (32 - __builtin_clz(n))), tag(2 << (32 - __builtin_clz(n))){}
    
    void pull(int p) {
        info[p] = info[p << 1] + info[p << 1 | 1];
    }

    void apply(int p, const Tag &t) {
        info[p].apply(t);
        tag[p].apply(t);
    }

    void push(int p) {
        apply(p << 1, tag[p]);
        apply(p << 1 | 1, tag[p]);
        tag[p] = Tag();
    }

    template<class T>
    LazySegmentTree(std::vector<T>& _init): LazySegmentTree(_init.size()) {
        auto build = [&](auto&& self, int p, int l, int r) {
            if (l == r) {
                info[p] = { _init[l] };
                return;
            }
            int mid = l + r >> 1;
            self(self, p << 1, l, mid), self(self, p << 1 | 1, mid + 1, r);
            pull(p);
        };
        build(build, 1, 0, n - 1);
    }

    Info rangeQuery(int p, int l, int r, int x, int y) {
        if (l > y || r < x) return Info();
        if (x <= l && r <= y) 
            return info[p];
        push(p);
        int mid = l + r >> 1;
        return rangeQuery(p << 1, l, mid, x, y) + rangeQuery(p << 1 | 1, mid + 1, r, x, y);
    }

    Info rangeQuery(int l, int r) {
        return rangeQuery(1, 0, n - 1, l, r);
    }

    void rangeApply(int p, int l, int r, int x, int y, const Tag &v){
        if (l > y || r < x) return;
        if (x <= l && r <= y) { 
            apply(p, v);
            return;
        }

        int mid = l + r >> 1;
        push(p);
        rangeApply(p << 1, l, mid, x, y, v);
        rangeApply(p << 1 | 1, mid + 1, r, x, y, v);
        pull(p);
    }

    void rangeApply(int l, int r, const Tag &v) {
        rangeApply(1, 0, n - 1, l, r, v);
    }
};

struct Tag {
    i64 add = 0;

    void apply(const Tag& t) {
        add += t.add;
    }
};

struct Info {
    i64 mi = inf64;
    void apply(const Tag &t) {
        mi += t.add;
    }
};

Info operator + (const Info &x, const Info &y) {
    return { std::min(x.mi, y.mi) };
}

struct Edge{
	int v, w;
};

void solve(){
	int n, q;
	std::cin >> n >> q;

	std::vector<Edge> e;
	std::vector<std::vector<int>> g(n);
	std::vector<i64> up(n);

	for (int i = 0, u, v, w, ed = 2 * n - 2; i < ed; ++ i) {
		std::cin >> u >> v >> w;
		-- u, -- v;
		if (v) {
			e.emplace_back(v, w);
			g[u].push_back(i);
		}
		else {
			e.emplace_back(u, w);
			up[u] = w;
		}
	}


	std::vector<int> dfn(n), sz(n);
	std::vector<i64> d(n);
	int cur = 0;
	
	auto dfs = [&](auto &&self, int u) -> void {
		dfn[u] = cur ++, sz[u] = 1;
		for (int i : g[u]) {
			auto [v, w] = e[i];
			d[v] = d[u] + w;
			self(self, v);
			sz[u] += sz[v];
		}
	};

	dfs(dfs, 0);

	std::vector<i64> val(n, -1);
	for (int i = 0; i < n; ++ i) {
		assert(val[dfn[i]] == -1);
		val[dfn[i]] = up[i] + d[i];
	}

	LazySegmentTree<Info, Tag> sgt(val);

	auto get = [&](int u) -> i64 {
		return sgt.rangeQuery(dfn[u], dfn[u]).mi;
	};

	for (int i = 0, op, x, y; i < q; ++ i) {
		std::cin >> op >> x >> y;
		if (op == 1) {
			if (x >= n) {
				int v = e[x - 1].v;
				sgt.rangeApply(dfn[v], dfn[v], Tag{y - up[v]});
				up[v] = y;
			}
			else {
				int v = e[x - 1].v;
				sgt.rangeApply(dfn[v], dfn[v] + sz[v] - 1, Tag{y - e[x - 1].w});
				e[x - 1].w = y;
			}
		}
		else {
			-- x, -- y;
			if (dfn[x] <= dfn[y] && dfn[y] <= dfn[x] + sz[x] - 1) {
				std::cout << get(y) - up[y] - get(x) + up[x] << '\n';
				continue;
			}
			std::cout << sgt.rangeQuery(dfn[x], dfn[x] + sz[x] - 1).mi + get(y) - up[y] - get(x) + up[x] << '\n';
		}
	}
}

auto FIO = []{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);
	return 0;
}();

int main() {
	#ifdef DEBUG
		freopen("in.txt", 'r', stdin);
		freopen("out.txt", 'w', stdout);
	#endif
	int t = 1;
	// std::cin >> t;
	while(t --)
		solve();
	return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQUINOX1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值