P4069 [SDOI2016] 游戏 Solution

Description

给定一棵有 nnn 个点的树 TTT,点有点权 AiA_iAi,边有边权,初始时 wi=123456789123456789w_i=123456789123456789wi=123456789123456789.
定义 dist⁡(s,t)\operatorname{dist}(s,t)dist(s,t)s→ts\to tst 路径上边权之和,path⁡(s,t)\operatorname{path}(s,t)path(s,t)s→ts\to tst 路径上点集.

执行 qqq 次操作,操作分两种:

  • modify⁡(s,t,a,b)\operatorname{modify}(s,t,a,b)modify(s,t,a,b):对每个 u∈path⁡(s,t)u\in \operatorname{path}(s,t)upath(s,t),执行 Au←min⁡(Au,a×dist⁡(s,u)+b)A_u\gets \min(A_u,a\times \operatorname{dist}(s,u)+b)Aumin(Au,a×dist(s,u)+b).
  • query⁡(s,t)\operatorname{query}(s,t)query(s,t):求 min⁡u∈path⁡(s,t)Ai\min\limits_{u\in\operatorname{path}(s,t)} A_iupath(s,t)minAi.

Limitations

1≤n,q≤1051\le n,q\le 10^51n,q105
∣a∣≤104,0≤w,∣b∣≤109|a|\le 10^4,0\le w,|b|\le 10^9a104,0w,b109
1s,250MB1\text{s},250\text{MB}1s,250MB

Solution

首先对 TTT 进行树链剖分.
注意到 y=a×dist⁡(s,u)+by=a\times \operatorname{dist}(s,u)+by=a×dist(s,u)+b 是一次函数的形式,但 dist⁡(s,u)\operatorname{dist}(s,u)dist(s,u)sss 变化而变化,无法直接维护.
考虑从 lca\text{lca}lca 处断开,拆成两条垂直链 s→lcas\to\text{lca}slcalca→t\text{lca}\to tlcat,设 dep⁡(u)\operatorname{dep}(u)dep(u)uuu 的带权深度,对每条链分别考虑:

  • u∈path⁡(s,lca)u\in\operatorname{path}(s,\text{lca})upath(s,lca) 则有 y=a×(dep⁡(s)−dep⁡(u))+b=−a×dep⁡(u)+(a×dep⁡(s)+b)y=a\times (\operatorname{dep}(s)-\operatorname{dep}(u))+b=-a\times \operatorname{dep}(u)+(a\times \operatorname{dep}(s) +b)y=a×(dep(s)dep(u))+b=a×dep(u)+(a×dep(s)+b).
  • u∈path⁡(lca,t)u\in \operatorname{path}(\text{lca}, t)upath(lca,t) 则有 y=a×(dep⁡(u)+dep⁡(s)−2dep⁡(lca))+b=a×dep⁡(u)+(a×dep⁡(s)+2a×dep⁡(lca)+b)y=a\times (\operatorname{dep}(u)+\operatorname{dep}(s)-2\operatorname{dep}(\text{lca}))+b=a\times \operatorname{dep}(u)+(a\times \operatorname{dep}(s) +2a\times\operatorname{dep}(\text{lca})+b)y=a×(dep(u)+dep(s)2dep(lca))+b=a×dep(u)+(a×dep(s)+2a×dep(lca)+b).

这两个都是关于 dep⁡(u)\operatorname{dep}(u)dep(u) 的一次函数.
由于从上往下 dep⁡(u)\operatorname{dep}(u)dep(u) 递增,直接用李超树维护,支持区间插入函数,求区间 min⁡y\min yminy 的值.
注意到最值一定在两个端点处,于是做完了,时间复杂度 O(qlog⁡3n)O(q\log^3 n)O(qlog3n),记得开 long long.

Code

4.5KB,3.08s,31.55MB  (C++20 with O2)4.5\text{KB},3.08\text{s},31.55\text{MB}\;\texttt{(C++20 with O2)}4.5KB,3.08s,31.55MB(C++20 with O2)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

constexpr i64 inf = 123456789123456789LL;

struct line {
	i64 k, b;
	inline line() {}
	inline line(i64 k, i64 b) : k(k), b(b) {}
};

inline int ls(int u) { return 2 * u + 1; }
inline int rs(int u) { return 2 * u + 2; }

template<class F>
struct segtree {
	struct node {
		int l, r;
		i64 val;
		line f;
	};
	vector<node> tr;
	F func;
	
	inline segtree() {}
	inline segtree(int n, const F& f) : tr(n << 1), func(f) {
		build(0, 0, n - 1);
	}
	
	void build(int u, int l, int r) {
	    tr[u].l = l, tr[u].r = r, tr[u].val = inf;
	    tr[u].f = line(0, inf);
	    
	    if (l == r) return;
	    const int mid = (l + r) >> 1;
	    build(ls(mid), l, mid);
	    build(rs(mid), mid + 1, r);
	}
	
	inline void pushup(int u, int mid) {
		chmin(tr[u].val, min(tr[ls(mid)].val, tr[rs(mid)].val));
	    chmin(tr[u].val, min(func(tr[u].f, tr[u].l), func(tr[u].f, tr[u].r)));
	}
	
	void defeat(int u, line li) {
		const int mid = (tr[u].l + tr[u].r) >> 1;
		if (func(li, mid) < func(tr[u].f, mid)) swap(li, tr[u].f);
		if (tr[u].l == tr[u].r) {
			tr[u].val = min(tr[u].val, min(func(tr[u].f, tr[u].l), func(tr[u].f, tr[u].r)));
			return;
		}
		if (func(li, tr[u].l) < func(tr[u].f, tr[u].l)) defeat(ls(mid), li);
		if (func(li, tr[u].r) < func(tr[u].f, tr[u].r)) defeat(rs(mid), li);
		pushup(u, mid);
	}
	
	void update(int u, int l, int r, line li) {
	    if (tr[u].r < l || r < tr[u].l) return;
	    if (l <= tr[u].l && tr[u].r <= r) return defeat(u, li);
	    
	    const int mid = (tr[u].l + tr[u].r) >> 1;
	    update(ls(mid), l, r, li);
		update(rs(mid), l, r, li);
		pushup(u, mid);
	}
	
	i64 query(int u, int l, int r) {
		if (tr[u].l > r || tr[u].r < l) return inf;
		if (l <= tr[u].l && tr[u].r <= r) return tr[u].val;
		
		const int mid = (tr[u].l + tr[u].r) >> 1;
		i64 ans = min(func(tr[u].f, max(l, tr[u].l)), func(tr[u].f, min(r, tr[u].r)));
		return min(ans, min(query(ls(mid), l, r), query(rs(mid), l, r)));
	}
};

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, q;
	cin >> n >> q;
	
	vector<vector<pair<int, int>>> g(n);
	for (int i = 0, u, v, w; i < n - 1; i++) {
		cin >> u >> v >> w, u--, v--;
		g[u].emplace_back(v, w);
		g[v].emplace_back(u, w);
	}
	
	vector<i64> dep(n);
	vector<int> par(n), siz(n), son(n, -1);
    
    auto dfs = [&](auto&& self, int u, int fa) -> void {
    	siz[u] = 1, par[u] = fa;
    	for (auto [v, w] : g[u]) {
    		if (v == fa) continue;
    		dep[v] = dep[u] + w;
    		self(self, v, u);
    		siz[u] += siz[v];
    		if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
    	}
    };
    
    int clock = 0;
    vector<int> dfn(n), top(n), rev(n);
    
    auto dfs2 = [&](auto&& self, int u, int topf) -> void {
        rev[dfn[u] = clock++] = u;
    	top[u] = topf;
		if (~son[u]) self(self, son[u], topf);
		for (auto [v, w] : g[u]) {
			if (v == par[u] || v == son[u]) continue;
			self(self, v, v);
		}
    };
    
    dfs(dfs, 0, 0);
    dfs2(dfs2, 0, 0);
    
    auto func = [&](const line& li, int x) { return li.k * dep[rev[x]] + li.b; };
    segtree<decltype(func)> tree(n, func);
    
    auto lca = [&](int u, int v) {
		while (top[u] != top[v]) {
			if (dep[top[u]] < dep[top[v]]) swap(u, v);
			u = par[top[u]];
		}
		if (dep[u] > dep[v]) swap(u, v);
		return u;
	};
	
	auto upd = [&](int u, int v, line li) {
		while (top[u] != top[v]) {
		    if (dep[top[u]] < dep[top[v]]) swap(u, v);
		    tree.update(0, dfn[top[u]], dfn[u], li);
		    u = par[top[u]];
		}
		if (dep[u] > dep[v]) swap(u, v);
		tree.update(0, dfn[u], dfn[v], li);
	};
	
	auto ask = [&](int u, int v) {
		i64 ans = inf;
		while (top[u] != top[v]) {
		    if (dep[top[u]] < dep[top[v]]) swap(u, v);
		    chmin(ans, tree.query(0, dfn[top[u]], dfn[u]));
		    u = par[top[u]];
		}
		if (dep[u] > dep[v]) swap(u, v);
		chmin(ans, tree.query(0, dfn[u], dfn[v]));
		return ans;
	};
	
	auto modify = [&](int u, int v, int a, int b) {
		int lc = lca(u, v);
		upd(u, lc, line(-a, a * dep[u] + b));
		upd(lc, v, line(a, dep[u] * a - 2 * dep[lc] * a + b));
	};
	
	for (int i = 0, op, u, v, a, b; i < q; i++) {
		cin >> op >> u >> v, u--, v--;
		if (op == 1) cin >> a >> b, modify(u, v, a, b);
		else cout << ask(u, v) << '\n';
	}
	
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值