题解 P10113 [GESP202312 八级] 大量的工作沟通
难度约为绿。
前置知识:倍增求最近公共祖先。
思路
将领导关系抽象为一棵树,要求的就是
定义
显然对于每次合作,如果不考虑编号,则
因此我们可以先求出
可以通过最近公共祖先的性质,求得
已知最近公共祖先后,所有能成为主持者的人必定都在
在 dfs 时预处理前缀最大值,就能在后面的询问中
总时间复杂度为
具体细节请看代码。
代码
#include<bits/stdc++.h>
using namespace std;
int n,q,m;
int fa[100003][30];
int mx[100003],dep[100003];
int l2[100003];
vector<int>edg[100003];
void dfs(int x,int f,int mxn){
if(x>mxn)mxn=x;
mx[x]=mxn; dep[x]=dep[f]+1;//预处理前缀最大值和深度
if(x!=0){
fa[x][0]=f;
for(int i=1;i<=l2[dep[x]];i++)
fa[x][i]=fa[fa[x][i-1]][i-1];//预处理2^i级祖先
}
for(int i=0;i<edg[x].size();i++)
dfs(edg[x][i],x,mxn);//向下递归
}
int lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
while(dep[a]>dep[b]){//调平深度
a=fa[a][l2[dep[a]-dep[b]]];
}
if(a==b)return a;//特判
for(int i=l2[dep[a]];i>=0;i--){//求祖先
if(fa[a][i]!=fa[b][i]){
a=fa[a][i];
b=fa[b][i];
}
}
return fa[a][0];
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
l2[0]=-1;
for(int i=1;i<n;i++){
l2[i]=l2[i>>1]+1;//预处理对数
int f; cin>>f;
edg[f].push_back(i);//连边
}
dfs(0,0,0);
cin>>q;
while(q--){
int F,x;
cin>>m>>F;
while(--m){
cin>>x;
F=lca(F,x);
}
cout<<mx[F]<<'\n';
}
return 0;
}