【BZOJ4515】【SDOI2016】游戏

本文介绍了一种结合树链剖分和李超线段树的数据结构算法,用于解决特定类型的问题。该算法能够高效地处理涉及树形结构上的区间更新和查询操作。通过树链剖分降低问题复杂度,并利用李超线段树进行快速更新和查询,实现了优秀的运行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目链接】

【思路要点】

  • 先树链剖分,然后用李超线段树维护序列,支持一段区间对某一次函数取Min,以及查询区间最小值。
  • 由于需要区间定位,单次1号操作的时间复杂度为\(O(Log^3N)\),单次2号操作的时间复杂度为\(O(Log^2N)\)。
  • 总时间复杂度\(O(MLog^3N)\),但数据没有将该做法的复杂度卡满,实际上做到这一点也很难,因此该做法可以通过本题,甚至跑得很快。

代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int MAXP = 200005;
const long long INF = 123456789123456789ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct SegmentTree {
	struct Node {
		int lc, rc;
		long long k, b, Min;
	} a[MAXP];
	int root, size, n;
	long long pos[MAXN];
	void build(int &root, int l, int r) {
		root = ++size;
		a[root].k = 0;
		a[root].b = INF;
		a[root].Min = INF;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	void update(int root, int l, int r) {
		a[root].Min = min(a[root].k * pos[l] + a[root].b, a[root].k * pos[r] + a[root].b);
		if (a[root].lc) chkmin(a[root].Min, a[a[root].lc].Min);
		if (a[root].rc) chkmin(a[root].Min, a[a[root].rc].Min);
	}
	void insert(int root, int l, int r, int ql, int qr, long long k, long long b) {
		if (a[root].k * pos[l] + a[root].b <= k * pos[l] + b && a[root].k * pos[r] + a[root].b <= k * pos[r] + b) return;
		if (l == ql && r == qr) {
			if (a[root].k * pos[l] + a[root].b >= k * pos[l] + b && a[root].k * pos[r] + a[root].b >= k * pos[r] + b) {
				a[root].k = k; a[root].b = b;
				update(root, l, r);
				return;
			}
			int mid = (l + r) / 2;
			if (a[root].k * pos[mid] + a[root].b > k * pos[mid] + b) {
				swap(a[root].k, k);
				swap(a[root].b, b);
			}
			insert(a[root].lc, l, mid, ql, mid, k, b);
			insert(a[root].rc, mid + 1, r, mid + 1, qr, k, b);
			update(root, l, r);
			return;
		}
		int mid = (l + r) / 2;
		if (ql <= mid) insert(a[root].lc, l, mid, ql, min(mid, qr), k, b);
		if (mid + 1 <= qr) insert(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, k, b);
		update(root, l, r);
	}
	void insert(int l, int r, long long k, long long b) {
		b -= pos[l] * k;
		insert(root, 1, n, l, r, k, b);
	}
	long long query(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].Min;
		int mid = (l + r) / 2;
		long long ans = min(a[root].k * pos[ql] + a[root].b, a[root].k * pos[qr] + a[root].b);
		if (mid >= ql) chkmin(ans, query(a[root].lc, l, mid, ql, min(mid, qr)));
		if (mid + 1 <= qr) chkmin(ans, query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr));
		return ans;
	}
	long long query(int l, int r) {
		return query(root, 1, n, l, r);
	}
} ST;
struct edge {int dest, len; };
vector <edge> a[MAXN];
int n, m, timer, up[MAXN], dfn[MAXN];
int depth[MAXN], father[MAXN], size[MAXN], son[MAXN];
long long tsum, lastpos, toroot[MAXN];
void efs(int pos, int fa) {
	dfn[pos] = ++timer; up[pos] = fa;
	ST.pos[timer] = tsum + toroot[pos] - lastpos;
	if (son[pos]) efs(son[pos], fa);
	else tsum += toroot[pos] - lastpos;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (a[pos][i].dest != father[pos] && a[pos][i].dest != son[pos]) {
			lastpos = toroot[pos];
			efs(a[pos][i].dest, a[pos][i].dest);
		}
}
void dfs(int pos, int fa) {
	size[pos] = 1;
	father[pos] = fa;
	depth[pos] = depth[fa] + 1;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (a[pos][i].dest != fa) {
			toroot[a[pos][i].dest] = toroot[pos] + a[pos][i].len;
			dfs(a[pos][i].dest, pos);
			size[pos] += size[a[pos][i].dest];
			if (size[a[pos][i].dest] > size[son[pos]]) son[pos] = a[pos][i].dest;
		}
}
int lca(int x, int y) {
	while (up[x] != up[y]) {
		if (depth[up[x]] < depth[up[y]]) swap(x, y);
		x = father[up[x]];
	}
	if (depth[x] < depth[y]) return x;
	else return y;
}
long long dist(int x, int y) {
	return toroot[x] + toroot[y] - 2 * toroot[lca(x, y)];
}
void insert(int x, int y, long long k, long long b) {
	long long dx = 0, dy = dist(x, y);
	while (up[x] != up[y]) {
		if (depth[up[x]] > depth[up[y]]) {
			dx += toroot[x] - toroot[up[x]];
			ST.insert(dfn[up[x]], dfn[x], -k, b + dx * k);
			dx += toroot[up[x]] - toroot[father[up[x]]];
			x = father[up[x]];
		} else {
			dy -= toroot[y] - toroot[up[y]];
			ST.insert(dfn[up[y]], dfn[y], k, b + dy * k);
			dy -= toroot[up[y]] - toroot[father[up[y]]];
			y = father[up[y]];
		}
	}
	if (dfn[x] < dfn[y]) ST.insert(dfn[x], dfn[y], k, b + dx * k);
	else ST.insert(dfn[y], dfn[x], -k, b + dy * k);
}
long long query(int x, int y) {
	long long ans = INF;
	while (up[x] != up[y]) {
		if (depth[up[x]] < depth[up[y]]) swap(x, y);
		chkmin(ans, ST.query(dfn[up[x]], dfn[x]));
		x = father[up[x]];
	}
	if (dfn[x] > dfn[y]) swap(x, y);
	chkmin(ans, ST.query(dfn[x], dfn[y]));
	return ans;
}
int main() {
	read(n), read(m);
	ST.init(n);
	for (int i = 1; i <= n - 1; i++) {
		int x, y, z;
		read(x), read(y), read(z);
		a[x].push_back((edge) {y, z});
		a[y].push_back((edge) {x, z});
	}
	dfs(1, 0);
	efs(1, 1);
	for (int i = 1; i <= m; i++) {
		int opt, x, y;
		read(opt), read(x), read(y);
		if (opt == 1) {
			long long b, k;
			read(k), read(b);
			insert(x, y, k, b);
		} else writeln(query(x, y));
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值