线段树 + 树形换根 + dfs序 ---- 离线启发式求解 (有点像树上启发式合并答案) F. Nearest Leaf

题目链接


题目大意:

就是给你一个有根树,每个点都有一个编号,编号是它们的 d f s dfs dfs序,现在 q q q次询问,每次询问给你一个点 v v v和一个区间 [ l , r ] [l,r] [l,r],问你,这个区间里面的叶子节点,距离 v v v最近的距离是多少?

每条边是有边权的

n , q ∈ [ 1 , 5 e 5 ] n,q\in[1,5e5] n,q[1,5e5]


解题思路:

  1. 首先我开始想在线处理但是太麻烦了。
  2. 对于这么多询问的还不强制在线的多半是离线处理!!
  3. 根据 d f s dfs dfs序我们很容易联想到线段树,唉这里还有个区间查询。
  4. 那么我们可以把询问按照 u u u进行分类,假设 u u u为根就是线段树里面查询 [ l , r ] [l,r] [l,r]的最小值
  5. 但是你去枚举根的时候很块超时了
  6. 那么我们就想到了换根
  7. 我们看一下每个节点 n o w now now和父亲节点的关系,就是从 f a → n o w fa\rightarrow now fanow的时候, n o w now now这个子树里面的所有的叶子节点都要减掉 e d g e [ f a → n o w ] . w edge[fa\rightarrow now].w edge[fanow].w,其他区间加上这个 w w w
  8. 那么这个问题就愉快的解决了 t i m e : O ( ( n + q ) l o g n ) time:O((n+q)logn) time:O((n+q)logn)

AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}
struct node {
	int id, l, r;
};
int n, q;
vector<node> G[maxn];
ll ans[maxn];
vector<pair<int,ll> > mp[maxn];

struct Segtree {
	ll lazy[maxn << 2], tr[maxn << 2], arr[maxn];
	void pushup(int rt) {tr[rt] = min(tr[rt<<1],tr[rt<<1|1]);}
	void pushdown(int rt) {
		lazy[rt<<1] += lazy[rt];  
		lazy[rt<<1|1] += lazy[rt];  
		tr[rt<<1] += lazy[rt]; 
		tr[rt<<1|1] += lazy[rt];
	    lazy[rt] = 0; 
	}
	void build(int rt, int l, int r) {
		lazy[rt] = 0;
		if(l == r) {
			tr[rt] = arr[l];
			return;
		}
		build(Lson);
		build(Rson);
		pushup(rt);
	}
	void update(int rt, int l, int r, int posl, int posr,ll val) {
		if(posl > posr) return;
		if(posl <= l && posr >= r) {
			tr[rt] += val;
			lazy[rt] += val;
			return;
		}
		pushdown(rt);
		if(posl <= mid) update(Lson,posl,posr,val);
		if(posr > mid) update(Rson,posl,posr,val);
		pushup(rt);
	}
	ll ask(int rt, int l, int r, int posl, int posr) {
		if(posl <= l && posr >= r) return tr[rt];
		pushdown(rt);
		ll res = LLF;
		if(posl <= mid) res = min(res,ask(Lson,posl,posr));
		if(posr > mid) res = min(res,ask(Rson,posl,posr)); 
		return res;
	}
}sgt;

int L[maxn], R[maxn], tot;
inline void dfs(int u, int fa, ll length) {
	sort(mp[u].begin(),mp[u].end());
	L[u] = ++ tot;
	for(auto it : mp[u]) {
		if(it.first == fa) continue;
		dfs(it.first,u,length+it.second);
	}
	R[u] = tot;
	if(L[u] == R[u]) sgt.arr[u] = length;//处理出所有叶子节点
	else sgt.arr[u] = LLF;
}

inline void dfs2(int u, int fa) {
	for(auto it : G[u]) ans[it.id] = sgt.ask(1,1,n,it.l,it.r);
	for(auto it : mp[u]) {
		if(it.first == fa) continue;
		sgt.update(1,1,n,1,L[it.first]-1,it.second); // [1,L-1]
		sgt.update(1,1,n,R[it.first]+1,n,it.second); // [R+1,n]
		sgt.update(1,1,n,L[it.first],R[it.first],-it.second); // [L,R]
		dfs2(it.first,u);
		sgt.update(1,1,n,1,L[it.first]-1,-it.second);// 还原
		sgt.update(1,1,n,R[it.first]+1,n,-it.second);
		sgt.update(1,1,n,L[it.first],R[it.first],it.second);
	}
}

int main() {
	IOS;
	cin >> n >> q;
	for(int i = 2; i <= n; ++ i) {
		int u;
		ll v;
		cin >> u >> v;
		mp[u].push_back({i,v});
		mp[i].push_back({u,v});
	}
	for(int i = 1; i <= q; ++ i) {
		int tag, l, r;
		cin >> tag >> l >> r;
		G[tag].push_back({i,l,r});
	}
	dfs(1,0,0);
	sgt.build(1,1,n);//建树
	dfs2(1,0);//求答案
	for(int i = 1; i <= q; ++ i) cout << ans[i] << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值