好久没有做虚树了,感觉好虚啊
首先建出虚树,然后对于两边dp搞出虚树上每个点连接到的居委会
对于虚树上的每条边,我们找到两端点的分界点,再对他们连接到的居委会更新答案
这里给树剖加个特技就能求两点间第K个点了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=300000+5;
const int inf=1e9;
int read(){
char ch=getchar();int x=0,f=1;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Edge{int to,next;}e[N<<1];
int head[N],cnt;
void ins(int u,int v){
if(u==v)return;
e[++cnt]=(Edge){v,head[u]};head[u]=cnt;
}
void insert(int u,int v){
ins(u,v);ins(v,u);
}
bool is[N];
int dep[N],fa[N],siz[N],son[N],top[N],pos[N],node[N],sz,dfn[N],dfs_clock;
void dfs(int u){
siz[u]=1;son[u]=0;dfn[u]=++dfs_clock;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;if(v==fa[u])continue;
fa[v]=u;dep[v]=dep[u]+1;
dfs(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs(int u,int tp){
top[u]=tp;pos[u]=++sz;node[sz]=u;
if(son[u])dfs(son[u],tp);
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v!=fa[u]&&v!=son[u])dfs(v,v);
}
}
bool cmp(int u,int v){
if(dep[u]==dep[v])return u<v;
return dep[u]<dep[v];
}
int lca(int u,int v){
while(top[u]!=top[v])
if(dep[top[u]]>dep[top[v]])u=fa[top[u]];
else v=fa[top[v]];
return min(u,v,cmp);
}
int dis(int u,int v){
if(!u||!v)return inf;
return dep[u]+dep[v]-2*dep[lca(u,v)];
}
int lin[N],f[N];
void dp1(int u){
f[u]=0;
if(is[u])lin[u]=u;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
dp1(v);lin[u]=min(lin[u],lin[v],cmp);
}
}
void dp2(int u){
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
int t1=dis(lin[e[i].to],e[i].to),t2=dis(lin[u],e[i].to);
if(t1==t2)lin[e[i].to]=min(lin[e[i].to],lin[u]);
else if(t2<t1)lin[e[i].to]=lin[u];
dp2(v);
}
}
int size_chain(int u){
return dep[u]-dep[top[u]]+1;
}
int iabs(int x){
return x<0?-x:x;
}
int size_chain(int u,int v){
return iabs(dep[u]-dep[v])+1;
}
int size(int u,int v){
return dis(u,v)+1;
}
int find(int u,int k){
if(k<=size_chain(u))return node[pos[u]-k+1];
else return find(fa[top[u]],k-size_chain(u));
}
int find(int u,int v,int k){
int w=lca(u,v);
if(k<=size_chain(u,w))return find(u,k);
else return find(v,size(u,v)-k+1);
}
int st[N],h[N],b[N];
bool cmp1(int u,int v){
return dfn[u]<dfn[v];
}
int work(int u,int v){
int x=lin[u],y=lin[v],mid=find(y,x,size(x,y)>>1),t=find(u,v,2);
if((size(x,y)&1)&&y<x)mid=fa[mid];
if(size(x,mid)<=size(x,u))mid=t;
else if(size(y,mid)<=size(y,v))mid=v;
f[x]+=siz[t]-siz[mid];f[y]+=siz[mid]-siz[v];
}
void calc_edge(int u){
f[lin[u]]+=siz[u];
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
f[lin[u]]-=siz[find(u,v,2)];
work(u,v);
calc_edge(v);
}
head[u]=0;lin[u]=0;
}
void solve(){
int k=read();
for(int i=1;i<=k;i++){
h[i]=read();is[h[i]]=true;
b[i]=h[i];
}
sort(h+1,h+1+k,cmp1);
int tp;cnt=0;
st[tp=1]=1;
for(int i=1;i<=k;i++){
int now=h[i],f=lca(now,st[tp]);
if(f==st[tp]){st[++tp]=now;continue;}
while(tp>1&&f==lca(now,st[tp-1])){
ins(st[tp-1],st[tp]);tp--;
f=lca(now,st[tp]);
}
ins(f,st[tp]);
st[tp]=f;st[++tp]=now;
}
while(--tp)ins(st[tp],st[tp+1]);
dp1(1);dp2(1);calc_edge(1);
for(int i=1;i<=k;i++){
printf("%d ",f[b[i]]);
is[b[i]]=false;
}
putchar('\n');
}
int main(){
//freopen("a.in","r",stdin);
int n=read();
for(int i=1;i<n;i++){
int u,v;u=read();v=read();insert(u,v);
}
dfs(1);dfs(1,1);dep[0]=inf;
memset(head,0,sizeof(head));
int q=read();
while(q--)
solve();
return 0;
}
突然发现是RANK6哎,果然树剖就是快