G. Unusual Entertainment 题解

[ G. Unusual Entertainment ]( Problem - 1899G - Codeforces )

题意描述

给一棵n个顶点,n-1条边的树,再给你一个长度为n的排列p,然后q次询问,每次询问对于排列p的区间$ [ l , r ] $中是否存在至少一个顶点是以x为根的子树的叶子节点。

刚看题的时候还以为会用Dij或者LCA做的,结果最后看了眼题解,发现有4种解法,我是一个也没对上啊(悲

解题思路

前置知识:不懂的建议先去学习一下

dfn序:将树化为dfn序的排列,以x为根的子树的顶点就在区间$[dfn[x],dfn[x]+sz[x]-1]$中。

离线树状数组区间查询:本质是一个差分。

思路

首先先得到树的dfn序,然后查询$[dfn[x],dfn[x]+sz[x]-1]$中是否出现$[p_l,……,p_r]$中的至少一个。

问题是怎么查询?

如果是对于一个普通的数组,想要查询区间$[l,r]$中是否出现至少一个数,我们可以使用差分数组求前缀和,然后如果$sum[r]-sum[l-1]>0$,就说明存在。

那么对于这道题,我们查询的区间是dfn序的,所以我们加点的时候可以加点的dfn序,然后我们只需要判断是否$ans[r]-ans[l-1]>0$即可。

另外,对于这种多次区间查询操作肯定是要离线的不要忘了,存储的时候要记录端点和查询区间,然后对端点从小到大排序,然后从1 ~ n遍历一边加点一边查询就行了~

代码

#include <bits/stdc++.h>
using namespace std;
//-------------------------------------------------------------------------------------------
#define int long long 
#define lost_R ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define P pair<int,int>
#define lowbit(x) (x&(-x))
#define dbg1(x) cout<<"# "<<x<<endl
#define dbg2(x,y) cout<<"# "<<x<<" "<<y<<endl
#define endl '\n'
const int mod=998244353;
const int N=1e5+10;
const int INF=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
using ar3=array<int,3>;
using ar2=array<int,2>;
//--------------------------------------------------------------------------------------
int n,m;
int a[N];
vector<int> g[N];
struct info{
    int p,l,r,id;
    bool operator < (const info& t) const{
        return p<t.p;
    }
}res[N<<1];
int c[N],dfn[N],sz[N],tot;
int ans[N<<1];
void dfs(int x,int f){
    dfn[x]=++tot;
    sz[x]=1;
    for(auto y:g[x]){
        if(y==f) continue;
        dfs(y,x);
        sz[x]+=sz[y];
    }
}
void add(int x,int v){
    for(;x<N;x+=lowbit(x)) c[x]+=v;
}
int query(int x){
    int sum=0;
    for(;x;x-=lowbit(x)) sum+=c[x];
    return sum;
}
int query(int l,int r){
    return query(r)-query(l-1);
}
void solve(){
    cin>>n>>m;
    tot=0;
    for(int i=1;i<=n;i++){
        g[i].clear();
        c[i]=0;  
    }
    for(int i=1;i<=n-1;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    dfs(1,1);
    int cnt=0;
    for(int i=1;i<=m;i++){
        int l,r,x;
        cin>>l>>r>>x;
        res[++cnt]={l-1,dfn[x],dfn[x]+sz[x]-1,cnt};
        res[++cnt]={r,dfn[x],dfn[x]+sz[x]-1,cnt};
    }
    sort(res+1,res+1+cnt);
    int rt=1;
    while(res[rt].p==0) ans[res[rt].id]=0,rt++;//如果端点为0不需要查询,直接标记为0即可,不然会WA的qwq
    for(int i=1;i<=n;i++){
        add(dfn[a[i]],1);
        while(res[rt].p==i&&rt<=cnt){//每个端点为i的都要记录查询
            ans[res[rt].id]=query(res[rt].l,res[rt].r);
            rt++;
        }
    }
    for(int i=1;i<=m;i++){
        if(ans[i*2]-ans[i*2-1]>0) cout<<"YES"<<endl;//这里的i*2就是l,i*2+1就是r
        else cout<<"NO"<<endl;
    }
}
signed main(){
    lost_R;
    // freopen("jia.in","r",stdin);
    // freopen("jia.out","w",stdout);
    int T=1;
    cin>>T;
    for(int i=1;i<=T;i++){
        solve();
        cout<<endl;
    }
    return 0;
}
posted @ 2025-07-30 13:24  RYRYR  阅读(6)  评论(0)    收藏  举报