一些题解

G
树上DFS + set启发式合并

题意:

给定一棵树和一个排列p , 给定若干次询问 每次询问给出l,r,x
求是否p[l]~p[r]有一个结点的祖先是x

思路:

不妨做个映射,把每个结点的编号映射为它在排列中的下标
那么转化为求x的子树中是否有l~r结点存在
考虑怎么判断:
对于给定的l,r,x ,若知道x的子树结点的集合,那么只需二分判断是否存在>=l并且<=r的元素存在即可

我们做一个DFS , 每次找出每个结点的重儿子,先将它的set转移而非合并到此节点的set中,其他节点的set暴力合并即可

复杂度O(n(logn)^2)

void solve(){
    int n,q;
    cin>>n>>q;
    vector<pii>edge;rep(i,1,n-1){int u,v;cin>>u>>v;edge.pb({u,v});}
    vector<int>p(n+1);
    vector<vector<pair<pii,int>>>qr(n+1);
    rep(i,1,n){int x;cin>>x;p[x]=i;}
    rep(i,1,q){int l,r,x;cin>>l>>r>>x;qr[p[x]].pb({{l,r},i});}
    vector<set<int>>s(n+1);
    vector<vector<int>>e(n+1);
    vector<int>ans(q+1,0);
    for(auto[l,r]:edge){e[p[l]].pb(p[r]);e[p[r]].pb(p[l]);}
    auto dfs =[&](auto&&self,int u,int fa)->void{
        int h=u;
        for(int v:e[u]){
            if(v==fa)continue;
            self(self,v,u);
            if(s[v].size()>s[h].size())h=v;
        }
        if(h!=u)swap(s[u],s[h]);
        s[u].insert(u);
        for(int v:e[u]){
            if(v==fa||v==h)continue;
            s[u].insert(s[v].begin(),s[v].end());
        }
        for(auto[PII,idx]:qr[u]){
            auto[l,r]=PII;
            auto px = s[u].lower_bound(l);
            if(px!=s[u].end() && (*px)<=r)ans[idx]=1;
        }
    };
    dfs(dfs,p[1],p[1]);
    rep(i,1,q){if(ans[i]==1)cout<<"YES"<<endl;else cout<<"NO"<<endl;}
}
posted @ 2025-10-23 22:16  Marinaco  阅读(6)  评论(0)    收藏  举报
//雪花飘落效果