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;
}

浙公网安备 33010602011771号