好久没有做虚树了,感觉好虚啊
首先建出虚树,然后对于两边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哎,果然树剖就是快