其他分享
首页 > 其他分享> > [LNOI2014] LCA(树链剖分+主席树+标记永久化)

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

作者:互联网

题意

给一棵 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