E. Tree Queries
题意
给定一颗 n n n 个顶点的树和 q q q 次询问,每次询问:
- x k a 1 , a 2 , . . . , a k x \hspace{4pt} k \hspace{4pt} a_1,a_2,...,a_k xka1,a2,...,ak:问把 k k k 个点删除后,距离 x x x 最远的点是多远
思路
先将所有查询离线化:对于每个查询,以 ( i , (i, (i, { a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak } ) ) ) 的形式存到 Q [ x ] Q[x] Q[x] 中。对于当前的点 x x x ,某次查询删除的点会造成一些阻塞,如果以初始 1 1 1 为根的话,阻塞分为两种情况:
- 如果节点 v v v 是 x x x 的祖先( v v v 在 1 → x 1 \rightarrow x 1→x 的路径上),那么从 x x x 出发最多向上爬到 v v v 的某一个儿子节点就不能向上爬了。不妨将这个儿子节点记为 n x t [ v ] nxt[v] nxt[v] ,表示 v → x v \rightarrow x v→x 的路径上第一个经过的节点,那么这种情况下除了 n x t [ v ] nxt[v] nxt[v] 的子树内的点可以访问到,其余点都会被阻塞。 n x t [ v ] nxt[v] nxt[v] 的赋值是在 d f s dfs dfs 的时候发生的,记录当前点 v v v 是往哪一个儿子搜下去的。
- 其他情况的话,只有 v v v 子树内的点(包括 v v v)会被阻塞,其他点都是可以访问的。
可以通过 d f s dfs dfs 序来将子树的点映射到一个连续的区间,情况 1 1 1 的阻塞区间就是: [ 1 , d f n [ n x t [ v ] ] − 1 ] [1,dfn[nxt[v]] - 1] [1,dfn[nxt[v]]−1] 和 [ u p [ n x t [ v ] ] + 1 , n ] [up[nxt[v]] + 1, n] [up[nxt[v]]+1,n],情况 2 2 2 的阻塞区间就是: [ d f n [ v ] , u p [ v ] ] [dfn[v],up[v]] [dfn[v],up[v]]。我们可以将所有被阻塞的区间统计下来,排个序之后就可以发现某些被分隔开来的可以被访问的连续区间,只需要在这些区间做区间查询一下最大距离即可。
如何快速查询最大距离?考虑换根,如果根换到当前被查询节点 x x x,且线段树上的所有点的值都表示到 x x x 的深度,就可以实现快速的查询。
若先前树根为 u u u,通过一条边 u − v u-v u−v 将树根换到 v v v,那么 v v v 的子树内的所有点的深度要 − 1 -1 −1,其余的节点的深度要 + 1 +1 +1。
代码中是先将 [ 1 , n ] [1,n] [1,n] 所有节点的深度 + 1 +1 +1,然后在新根的子树内 − 2 -2 −2,相当于抵消前一次的 + 1 +1 +1 后再 − 1 -1 −1。
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
const int INF=0x3f3f3f3e;
const long long INFLL=0x3f3f3f3f3f3f4000LL;
typedef long long ll;
const int N = 200050;
std::vector<int> g[N];
int tot;
int dfn[N];
int up[N];
int n,q;
struct node{
int Mdep;
int lazy;
}tree[N<<2];
int ans[N];
std::vector<std::pair<int,std::vector<int>>> Q[N];
int nxt[N];
void dfs1(int u,int fa){
dfn[u] = ++tot;
for(auto v : g[u])
if(v != fa){
dfs1(v,u);
}
up[u] = tot;
}
inline void push_down(int p){
int d = tree[p].lazy;
tree[p].lazy = 0;
tree[p<<1].lazy += d;
tree[p<<1].Mdep += d;
tree[p<<1|1].lazy += d;
tree[p<<1|1].Mdep += d;
}
void update(int p,int l,int r,int ql,int qr,int d){
if(ql <= l && r <= qr){
tree[p].Mdep += d;
tree[p].lazy += d;
return;
}
if(qr < l || r < ql) return;
int mid = l + r >> 1;
if(tree[p].lazy) push_down(p);
if(l <= mid) update(p<<1,l,mid,ql,qr,d);
if(r > mid) update(p<<1|1,mid+1,r,ql,qr,d);
tree[p].Mdep = std::max(tree[p<<1].Mdep , tree[p<<1|1].Mdep);
}
int query(int p,int l,int r,int ql,int qr){
if(ql <= l && r <= qr) return tree[p].Mdep;
if(qr < l || r < ql) return 0;
if(tree[p].lazy) push_down(p);
int mid = l + r >> 1;
int res = 0;
if(l <= mid) res = query(p<<1,l,mid,ql,qr);
if(r > mid) res = std::max(res,query(p<<1|1,mid+1,r,ql,qr));
return res;
}
void dfs2(int u,int fa){
for(auto& [i , a] : Q[u]){
std::vector<std::pair<int,int>> skip;
for(auto v : a){
if(dfn[v] < dfn[u] && dfn[u] <= up[v]){ // v是u的祖先
skip.push_back({1,dfn[nxt[v]]-1});
skip.push_back({up[nxt[v]] + 1, n});
}
else{
skip.push_back({dfn[v] , up[v]});
}
}
int pre = 1;
int res = 0;
std::sort(skip.begin() , skip.end());
for(auto [l,r] : skip){
if(pre < l) res = std::max(res , query(1,1,n,pre,l-1));
pre = std::max(pre,r+1); //这里要取max,因为某些阻塞区间可能存在包含关系
}
if(pre <= n) res = std::max(res,query(1,1,n,pre,n));
ans[i] = res;
}
update(1,1,n,1,n,1);
for(auto v : g[u]){
if(v == fa) continue;
nxt[u] = v;
update(1,1,n,dfn[v],up[v],-2);
dfs2(v,u);
update(1,1,n,dfn[v],up[v],2);
}
update(1,1,n,1,n,-1);
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::cin>>n>>q;
fore(i,1,n){
int u,v;
std::cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1,0);
fore(i,1,q+1){
int x,k;
std::cin>>x>>k;
std::vector<int> V;
fore(i,0,k){
int u;
std::cin>>u;
V.push_back(u);
}
Q[x].push_back({i,V});
}
fore(u,2,n+1) update(1,1,n,dfn[u],up[u],1);
dfs2(1,0);
fore(i,1,q+1) std::cout<<ans[i]<<endl;
return 0;
}