Codeforces Round 914 div. 2 E. Tree Queries

E. Tree Queries

E

题意

给定一颗 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 为根的话,阻塞分为两种情况:

  1. 如果节点 v v v x x x 的祖先( v v v 1 → x 1 \rightarrow x 1x 的路径上),那么从 x x x 出发最多向上爬到 v v v 的某一个儿子节点就不能向上爬了。不妨将这个儿子节点记为 n x t [ v ] nxt[v] nxt[v] ,表示 v → x v \rightarrow x vx 的路径上第一个经过的节点,那么这种情况下除了 n x t [ v ] nxt[v] nxt[v]子树内的点可以访问到,其余点都会被阻塞。 n x t [ v ] nxt[v] nxt[v] 的赋值是在 d f s dfs dfs 的时候发生的,记录当前点 v v v 是往哪一个儿子搜下去的。
  2. 其他情况的话,只有 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 uv 将树根换到 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值