[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∑rdeplca(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