题解 CF1110F 【Nearest Leaf】
Siyuan
2019-02-08 21:36:04
[$$\Large\texttt{My Blog}$$](https://hydingsy.github.io/articles/problem-Codeforces-1110F-Nearest-Leaf/)
---
## Description
> 题目链接:[Codeforces 1110F](https://codeforces.com/contest/1110/problem/F)
给你一棵带权有根树,根节点为 $1$,且保证每个点的父亲 $p_i<i$,其中 $(p_i,i)$ 的边权为 $w_i$。这棵树有个性质:如果我们 $\text{DFS}$ 这棵树,对于每个点都递增枚举儿子节点,每访问到一个节点就记录其编号,那么得到的序列刚好为 $1$ 到 $n$。
现在有 $q$ 次询问,每次给出 $v_i,l_i,r_i$,求从 $v_i$ 出发到 $[l_i,r_i]$ 中的其中一个叶子节点的最短距离(保证 $[l_i,r_i]$ 中至少有一个叶子节点)。
数据范围:$3\le n\le 5\times 10^5$,$1\le q\le 5\times 10^5$,$1\le p_i<i$,$1\le w_i\le 10^9$
------
## Solution
考虑都没有修改操作,我们可以把询问**离线**下来。按照询问的节点分类。
我们首先观察一个性质:由于 $\text{DFS}$ 序就是 $1$ 到 $n$,那么意味着**每棵子树内**的编号都是**连续**的,记子树 $i$ 内点的**最大编号**为 $k_i$。
我们再考虑一个问题:如果从 $u$ 到达 $v$(我们记这条边为 $(u,v,w)$),其中 $v$ 的深度大于 $u$,那么到达每个叶子的路径长度会发生什么变化?
- 对于在 $v$ 子树内的叶子,到他们的距离**减少** $w$。
- 其余叶子节点,到他们的距离**增加** $w$。
根据之前的性质,我们发现 $v$ 子树内的节点编号连续,可以直接用**线段树修改**。具体的修改方法为:我们将线段树内 $[1,n]$ 的节点的值增加 $w$,将 $[v,k_v]$ 的节点的值减少 $2w$。
为了防止对线段树中**非叶子节点**的影响,我们应该要将他们的值初始化为 $\text{INF}$,使得无论如何修改都不会影响答案。
这样一来,我们可以得到一个简单的算法流程:
1. 将 $1$ 到所有叶子节点的距离放到线段树中,非叶子节点的距离定义为 $\text{INF}$。
2. 直接 $\text{DFS}$ 整棵树,将询问节点为**当前节点**的询问统计答案。
3. 枚举当前节点 $u$ 的儿子节点 $v$,在线段树上修改 $[v,k_v]$ 的值并递归求解 $v$ 的值;递归后记得**回溯**消去影响!
**时间复杂度**:$O((n+q)\log n)$
------
## Code
```cpp
#include <cstdio>
#include <algorithm>
#include <vector>
#define lson p<<1
#define rson p<<1|1
const int N=5e5+5;
const long long INF=1LL<<60;
int n,m,tot,l[N],r[N],mx[N],lnk[N],ter[N],nxt[N],val[N];
long long seg[N<<2],tag[N<<2],dis[N],ans[N];
std::vector<int> q[N];
void pushup(int p) {
seg[p]=std::min(seg[lson],seg[rson]);
}
void pushdown(int p) {
if(!tag[p]) return;
long long v=tag[p];
seg[lson]+=v,tag[lson]+=v,seg[rson]+=v,tag[rson]+=v,tag[p]=0;
}
void modify(int x,int y,int p,int l,int r,long long v) {
if(x<=l&&r<=y) {
seg[p]+=v,tag[p]+=v;
return;
}
pushdown(p);
int mid=(l+r)>>1;
if(x<=mid) modify(x,y,lson,l,mid,v);
if(mid<y) modify(x,y,rson,mid+1,r,v);
pushup(p);
}
long long query(int x,int y,int p,int l,int r) {
if(x<=l&&r<=y) return seg[p];
pushdown(p);
int mid=(l+r)>>1;
long long ans=INF;
if(x<=mid) ans=std::min(ans,query(x,y,lson,l,mid));
if(mid<y) ans=std::min(ans,query(x,y,rson,mid+1,r));
return ans;
}
void add(int u,int v,int w) {
ter[++tot]=v,nxt[tot]=lnk[u],lnk[u]=tot,val[tot]=w;
}
void dfs1(int u) {
mx[u]=u;
for(int i=lnk[u];i;i=nxt[i]) {
int v=ter[i];
dis[v]=dis[u]+val[i];
dfs1(v);
mx[u]=std::max(mx[u],mx[v]);
}
}
void dfs2(int u) {
for(int i:q[u]) ans[i]=query(l[i],r[i],1,1,n);
for(int i=lnk[u];i;i=nxt[i]) {
int v=ter[i],w=val[i];
modify(1,n,1,1,n,w),modify(v,mx[v],1,1,n,-w-w);
dfs2(v);
modify(1,n,1,1,n,-w),modify(v,mx[v],1,1,n,w+w);
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=2;i<=n;++i) {
int p,w;
scanf("%d%d",&p,&w),add(p,i,w);
}
dfs1(1);
for(int i=1;i<=n;++i) {
modify(i,i,1,1,n,i==mx[i]?dis[i]:INF);
}
for(int i=1;i<=m;++i) {
int v;
scanf("%d%d%d",&v,&l[i],&r[i]),q[v].push_back(i);
}
dfs2(1);
for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
return 0;
}
```