泳池(skylines)(线段树)

题目

在这里插入图片描述
在这里插入图片描述

样例输入
5
7 7 6 9 9
2 5 5
2 3 1
4 1 1
1 2 2
4
2
5
3
4
样例输出
1
7
0
3
在这里插入图片描述

题解

  • 对于一条链的情况:
    • 假设我们当前要求的点为 u u u,那么我们可以建一棵线段树,树上每个节点 v v v表示 − c [ v ] + d i s ( u , v ) -c[v]+dis(u,v) c[v]+dis(u,v),在我们进行区间修改时,假设我们要更新的下一个点为 u ′ u' u,我们只需将这个点右边的点的权值减去 d i s ( u , u ′ ) dis(u,u') dis(u,u),左边的点的权值加上 d i s ( u ′ u ) dis(u'u) dis(uu)即可(画个图应该很好理解吧)
    • 询问时只需询问 1 1 1~ n n n中的最小值即可(不包括询问点的最小值)
  • 对于树的情况
    • 对于修改操作我们只需修改它子树内和子树外的范围,而判断这个范围我们可以借助 d f s dfs dfs序。
  • 注意
    • 我们用 d f s dfs dfs序时相当于将整个树重新编号,询问时要注意用该点的新编号
    • 子树外具体范围不好表示,故我们可以先将整棵树整体赋值,然后在处理子树内的部分
    • 在询问答案时,我将所有答案预处理出来,采用树的遍历的方式进行更新(因为想不到其他更新方式)

code

#include <bits/stdc++.h> 
using namespace std; 
const int maxn = 3e5 + 1000; 
typedef long long LL; 

template <typename T> 
inline void read(T &s) {
	s = 0; 
	T w = 1, ch = getchar(); 
	while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
	while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
	s *= w; 
}

int n, q, tot, cnt; 
int c[maxn], f[maxn], d[maxn]; 
int dfn[maxn], siz[maxn], lin[maxn], pre[maxn]; 
LL ans[maxn]; 
int num = 0; 
struct node {
	int next, to, dis; 
} e[maxn * 2]; 
struct tree {
	int l, r; 
	LL dat, add; 
} t[maxn * 4]; 

inline void add(int from, int to, int dis) {
	e[++tot].to = to; 
	e[tot].dis = dis; 
	e[tot].next = lin[from]; 
	lin[from] = tot; 
}

void dfs(int u, int fa) {
	siz[u] = 1; 
	dfn[u] = ++cnt; 
	pre[cnt] = u;  
	for (int i = lin[u]; i; i = e[i].next) {
		int v = e[i].to; 
		if (v == fa) continue; 
		d[v] = d[u] + e[i].dis;
		dfs(v, u); 
		siz[u] += siz[v]; 
	}
}

void build(int p, int l, int r) {
	t[p].l = l, t[p].r = r; 
	if (l == r) { 
		t[p].dat = d[pre[l]] - c[pre[l]]; 
		return ; 
	}
	int mid = (l + r) >> 1; 
	build(p<<1, l, mid); 
	build(p<<1|1, mid + 1, r); 
	t[p].dat = min(t[p<<1].dat, t[p<<1|1].dat); 
}

void spread(int p) {
	t[p<<1].dat += t[p].add; 
	t[p<<1|1].dat += t[p].add; 
	t[p<<1].add += t[p].add; 
	t[p<<1|1].add += t[p].add; 
	t[p].add = 0; 
}

void change(int p, int l, int r, int v) {
	if (l <= t[p].l && r >= t[p].r) {
		t[p].dat += 1ll * v; 
		t[p].add += 1ll * v; 
		return ; 
	}
	spread(p); 
	int mid = (t[p].l + t[p].r) >> 1; 
	if (l <= mid) change(p<<1, l, r, v); 
	if (r > mid) change(p<<1|1, l, r, v); 
	t[p].dat = min(t[p<<1].dat, t[p<<1|1].dat); 
}

LL query(int p, int l, int r) {
	if (l <= t[p].l && r >= t[p].r) return t[p].dat; 
	spread(p); 
	int mid = (t[p].l + t[p].r) >> 1; 
	LL val = 0x3f3f3f3f; 
	if (l <= mid) val = min(val, query(p<<1, l, r)); 
	if (r > mid) val = min(val, query(p<<1|1, l, r)); 
	return val; 
}

void work(int u, int fa) {
	for (int i = lin[u]; i; i = e[i].next) {
		int v = e[i].to; 
		if (v == fa) continue; 
		int dd = e[i].dis;
		change(1, 1, n, dd); 
		change(1, dfn[v], dfn[v] + siz[v] - 1, -2 * dd); 
		LL res = 0x3f3f3f3f; 
		if (dfn[v] > 1) 
			res = min(res, query(1, 1, dfn[v] - 1)); 
		if (dfn[v] < n) 
			res = min(res, query(1, dfn[v] + 1, n)); 
		ans[v] = res + 1ll * c[v]; 
		work(v, u); 
		change(1, 1, n, -dd); 
		change(1, dfn[v], dfn[v] + siz[v] - 1, 2 * dd); 
	}
}

signed main() {
//	freopen("skylines.in", "r", stdin); 
//	freopen("skylines.out", "w", stdout); 
	freopen("1.in", "r", stdin); 
	freopen("1.out", "w", stdout); 
	
	read(n); 
	for (int i = 1; i <= n; ++i) read(c[i]); 
	for (int i = 1; i < n; ++i) {
		int x, y, z; 
		read(x), read(y), read(z); 
		add(x, y, z); 
		add(y, x, z); 
	}
	
	d[1] = 0; 
	dfs(1, 0); 
	
	build(1, 1, n); 
	
	ans[1] = 1ll * query(1, 2, n) + c[1];  
	work(1, 0); 
	
	read(q); 
	for (int i = 1; i <= q; ++i) {
		int x; read(x); 
		printf("%lld\n", ans[x]); 
	}
	return 0; 
}
/*
5
7 7 6 9 9
2 5 5
2 3 1
4 1 1
1 2 2
4
2
5
3
4
*/

/*
5
7 7 6 9 9
2 5 5
2 3 1
4 1 1
1 2 2
1
5
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值