ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[LNOI2014] LCA(树链剖分+主席树+标记永久化)

2021-04-15 20:30:16  阅读:176  来源: 互联网

标签:rt f1 剖分 int LNOI2014 树链 dep dfn root


题意

给一棵 n n n 个点的树,有 m m m 次询问,每次询问给出 l , r , x l,r,x l,r,x,求 ∑ i = l r d e p l c a ( i , x ) \sum\limits_{i=l}^{r}dep_{lca(i,x)} i=l∑r​deplca(i,x)​。
其中, n , m ≤ 50000 n,m\le 50000 n,m≤50000。

分析

有个比较套路的思想,就是用将 [ l , r ] [l,r] [l,r] 中的点到根的路径都 + 1 +1 +1,然后求 x x x 到 1 1 1 的路径和就是答案。
于是我们可以用离线来做这道题,用 r r r 的贡献减去 l − 1 l-1 l−1 的贡献。具体来说,把询问拆成两个,一个是 ( x , r ) (x,r) (x,r),一个是 ( x , l − 1 ) (x,l-1) (x,l−1)。于是我们可以想到按下标从小到大的顺序枚举,每次将 i i i 到根的路径 + 1 +1 +1,这个可以用树链剖分+线段树解决。
当然,我们这里考虑强制在线的做法,需要用到主席树。我们令 r o o t i root_i rooti​ 为下标为 i i i 对应的线段树,当下标为 i + 1 i+1 i+1 时,我们在 r o o t i root_i rooti​ 的基础上将 i + 1 i+1 i+1 到根的路径都 + 1 +1 +1,动态开点加上标记永久化即可。每次更新会多 l o g n logn logn 个点,总共更新 n l o g n nlogn nlogn 次,因此空间要开 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n),时间复杂度也是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)。

代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e4 + 5;
vector<int> E[N];
int n, sz[N], son[N], dep[N], top[N], fa[N], dft, dfn[N], re[N];
void dfs1(int u){
	sz[u] = 1;
	for(int& v: E[u]){
		dep[v] = dep[u] + 1;
		fa[v] = u;
		dfs1(v);
		sz[u] += sz[v];
		if(sz[v] >= sz[son[u]]) son[u] = v;
	}
}
void dfs2(int u, int f){
	top[u] = f; dfn[u] = ++dft, re[dft] = u;
	if(son[u]) dfs2(son[u], f);
	for(int& v: E[u]) if(v != son[u]) dfs2(v, v);
}
int sum[N * 200], tag[N * 200], lch[N * 200], rch[N * 200], root[N], cnt;
#define lson l, m, lch[rt]
#define rson m + 1, r, rch[rt]
void update(int l, int r, int& rt, int las, int a, int b, int c){	
	rt = ++cnt;
	sum[rt] = sum[las] + c * (min(r, b) - max(a, l) + 1);
	lch[rt] = lch[las], rch[rt] = rch[las], tag[rt] = tag[las];
	if(l >= a && r <= b){
		tag[rt] += c;
		return;
	}
	int m = l + r >> 1;
	if(a <= m) update(lson, lch[las], a, b, c);
	if(b > m) update(rson, rch[las], a, b, c);
}
int query(int l, int r, int& rt, int a, int b){
	if(!rt) return 0;
	if(l >= a && r <= b) return sum[rt];
	int m = l + r >> 1, ans = tag[rt] * (min(r, b) - max(a, l) + 1);
	if(a <= m) ans += query(lson, a, b);
	if(b > m) ans += query(rson, a, b);
	return ans;
}
void update_chain(int i, int a, int b, int c){
	int f1 = top[a], f2 = top[b];
	while(f1 != f2){
		if(dep[f1] < dep[f2]) swap(f1, f2), swap(a, b);
		update(1, n, root[i], root[i], dfn[f1], dfn[a], c);
		a = fa[f1], f1 = top[a];
	}
	if(dep[a] < dep[b]) swap(a, b);
	update(1, n, root[i], root[i], dfn[b], dfn[a], c);
}
int query_chain(int i, int a, int b){
	debug(a, b);
	int f1 = top[a], f2 = top[b], ans = 0;
	while(f1 != f2){
		if(dep[f1] < dep[f2]) swap(f1, f2), swap(a, b);
		ans += query(1, n, root[i], dfn[f1], dfn[a]);
		a = fa[f1], f1 = top[a];
	}
	if(dep[a] < dep[b]) swap(a, b);
	ans += query(1, n, root[i], dfn[b], dfn[a]);
	return ans;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int m;
	cin >> n >> m;
	for(int i = 2, fa; i <= n; i++) cin >> fa, fa++, E[fa].push_back(i);
	dfs1(1);
	dfs2(1, 1);
	for(int i = 1; i <= n; i++) root[i] = root[i - 1], update_chain(i, i, 1, 1);
	for(int i = 1, l, r, x; i <= m; i++){
		cin >> l >> r >> x;
		l++, r++, x++;
		int ans = query_chain(r, x, 1) - query_chain(l - 1, x, 1);
		cout << ans % 201314 << '\n';
	}
	return 0;
}

标签:rt,f1,剖分,int,LNOI2014,树链,dep,dfn,root
来源: https://blog.csdn.net/iamhpp/article/details/115730139

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有