虚树
所谓虚树,就是我们在做题中把用到的点建到一颗新树上。
比如如果我们当前询问一条链的两端,如果我们直接dfs(暴力)的话时间复杂度就是O(n)
但是如果我们用虚树,那么这棵树上就只会有这两个节点
两个节点之间的那条边记录了原本链的信息
于是复杂度从O(n)为了O(2)
一般题中除了给出的询问点,我们还会用到询问点的lca,但是lca数也是o(n^2)的,于是我们想到把关键点按dfs序排序,
比如设有三个点x,y,z,,他们按dfs序排序,lca(y,z)一定在lca(x,y)或者lca(y,z)中
(因为,一棵树上k个点的lca不管怎么组合应该是不超过k-1个的)。
于是我们现在找到了2*k级别的点,问题是怎么把他们丢到一棵树上去。
我们先再次把所有点按dfs序排序,然后我们考虑这样一种方法 : 我们维护一个栈,如果栈为空,直接加点,否则我们看栈顶的点是不是当前点的祖先:
如果不是,那么弹出栈顶继续,否则就可以把当前点加进栈了(然后从栈顶向这个点连一条边)。
(因为之前插入的点可能是和当前点是并列关系,这个不一定会需要用到lca,因为我们记录一个dfs时入队和出队的顺序就可以了)。(第一个点就直接作为根了)。
代码实现
#include <bits/stdc++.h>
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define inf 100000000ll
#define N 200010
using namespace std;
inline int read(){
int ret=0,f=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())ret=ret*10+c-'0';
return ret*f;
}
struct pppp{int to,nxt,len;}a[N*3],b[N*10];
int in[N],out[N],dep[N],f[N][25],he[N],pp=0;
int n,tim=0,m,root,fir[N];
int sum=0,siz=0,len,top,li[N],que[N];
long long Dp[N][3];
bool instack[N],mark[N],sta[N];
bool comp(const int &x,const int &y){return in[x]<in[y];}
bool check(int x,int y){return in[x]<=in[y]&&out[x]>=out[y];};
inline void add(int x,int y){
a[++pp].to=y;a[pp].nxt=he[x];he[x]=pp;
a[++pp].to=x;a[pp].nxt=he[y];he[y]=pp;
}
inline void inser(int x,int y,int z) {
b[++siz].to=y;b[siz].nxt=fir[x];
fir[x]=siz;b[siz].len=z;
}
void dfs(int x,int de,int fa){
dep[x]=de;f[x][0]=fa;
in[x]=++tim;
for(int i=1;i<=20;++i)f[x][i]=f[f[x][i-1]][i-1];
for(int i=he[x];i;i=a[i].nxt)
if(dep[a[i].to]==0)dfs(a[i].to,de+1,x);
out[x]=++tim;
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i;--i)
if(dep[f[x][i]]>=dep[y])x=f[x][i];
if(x==y)return x;
for(int i=20;i;--i)
if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(){int x,y,Q;
n=read();
for(int i=1;i<n;++i){
x=read();y=read();
add(x,y);
}
dfs(1,1,1);
Q=read();
while(Q--){
m=read();len=m;top=0;root=0;
for(int i=1;i<=m;++i)li[i]=read(),que[i]=li[i];
sort(li+1,li+m+1,comp);
for(int i=1;i<m;++i)li[++len]=lca(li[i],li[i+1]);
sort(li+1,li+len+1,comp);
len=unique(li+1,li+len+1)-li-1;
for(int i=1;i<=len;++i){
while(top>0&&!check(sta[top],li[i]))--top;
if(sta[top]==0)root=li[i];
else{
int s=sta[top],t=li[i];
inser(s,t,dep[t]-dep[s]);
}
sta[++top]=li[i];
}
for(int i=1;i<=len;++i)instack[li[i]]=true;
for(int i=1;i<=m;++i)mark[que[i]]=true;
for(int i=1;i<=len;++i)instack[li[i]]=false;
for(int i=1;i<=m;++i)mark[que[i]]=false;
for(int i=1;i<=len;++i)fir[li[i]]=0;siz=0;
}
return 0;
}